Language overview
The Language basics section is a complete starting point, not a preview of every feature. After finishing it, you can write multi-file programs with typed functions, local state, structured data, datatypes, pattern matching, tests, and package dependencies.
Advanced abstraction features are kept in Advanced topics. Collection types and other reusable APIs are documented under the Standard library.
The shape of a Noodle program
Section titled “The shape of a Noodle program”A small program consists of declarations. Execution begins at a selected
source file’s main function:
func square(value : Int) -> Int do value * valueend
func main() -> Unit do answer = square(6); Console.println(answer.to_string());endThis short program already shows several important rules:
- function parameters and results have explicit types;
- local bindings such as
answerare inferred; =introduces a binding rather than assigning to an existing one;- intermediate statements end with
;; - the final expression of a value-producing block has no semicolon;
endcloses the function regardless of indentation.
Static types without global inference
Section titled “Static types without global inference”Noodle checks every expression before generating JavaScript. Reusable boundaries stay explicit, while local values are inferred from nearby information:
func discount(price : Int, percent : Int) -> Int do amount = price * percent / 100; price - amountendThere are no implicit conversions between distinct types. An Int does not
become a Double merely because another expression expects one.
Source syntax at a glance
Section titled “Source syntax at a glance”Noodle source files use UTF-8 and the .nl suffix. Spaces, tabs, and line
endings separate tokens, but indentation has no syntactic meaning. Line breaks
do not insert semicolons or close blocks.
Names are case-sensitive and use casing to communicate their role:
- lower names such as
total_pricename functions, parameters, bindings, and fields; - upper names such as
InvoiceandTname types and type parameters; - constants use uppercase snake case, such as
MAX_RETRIES; - a single
_discards a value and does not create a binding.
Keywords can be used in lower-name positions only when escaped with
backticks. For example, `end` is an ordinary name, while end closes a
construct.
The basic literal forms are:
42 Int0x2a Int, hexadecimal0b101010 Int, binary3.5 Double1e6 Doubletrue Bool'N' Char'😀' Char"Noodle" String() UnitA minus sign is an operator rather than part of a numeric literal. String and
character escapes include \n, \r, \t, \\, hexadecimal escapes such as
\x41, and Unicode escapes such as \u{1F600}.
Blocks contain semicolon-terminated statements followed by an optional tail
expression. A block without a tail expression produces Unit. An expression
statement discards its result; explicitly bind to _ when a non-Unit value
is intentionally ignored.
func rectangle_area(width : Int, height : Int) -> Int do checked_width = if width < 0 then 0 else width end; checked_height = if height < 0 then 0 else height end; checked_width * checked_heightendComments beginning with // document the declaration that follows. The raw
comment marker //\ is intended for temporarily commenting out source.
Top-level source files contain declarations such as functions, constants,
type aliases, datatypes, interfaces, extensions, and tests; arbitrary
statements do not run at the top level.
Expressions and explicit effects
Section titled “Expressions and explicit effects”Many constructs, including if and switch, produce values. State changes
use separate syntax: immutable bindings use =, while mutable storage uses
mut and :=. Expected failures and asynchronous tasks are also explicit;
their introductory guides are Error handling and
Asynchronous code.
A practical reading order
Section titled “A practical reading order”Read the basics in order:
- Basic types
- Values and bindings
- Functions
- Control flow
- Records and tuples
- Datatypes and basic patterns
- Error handling
- Asynchronous code
- Tests
- Modules and packages
The examples assume the default standard library is enabled. The Prelude and modules chapter later explains which names come from that library.