diff --git a/next/language/ffi-and-wasm-host.md b/next/language/ffi-and-wasm-host.md index 36d14118..0f0b2514 100644 --- a/next/language/ffi-and-wasm-host.md +++ b/next/language/ffi-and-wasm-host.md @@ -4,6 +4,11 @@ You can use foreign function in MoonBit through FFI to interact with the hosting ⚠ Warning: MoonBit is still in early stage, so the content may be outdated. +## Init function + +For WebAssembly backend, it means that it will be executed **before** the instance is available, meaning that the FFIs that relies on the instance's exportations can not be used at this stage; +for JavaScript backend, it means that it will be executed during the importation stage. + ## FFI ### Declare Foreign Reference diff --git a/next/language/fundamentals.md b/next/language/fundamentals.md index dcba979a..60a3200e 100644 --- a/next/language/fundamentals.md +++ b/next/language/fundamentals.md @@ -207,9 +207,56 @@ You can use `numbers[x]` to refer to the xth element. The index starts from zero :end-before: end array 2 ``` +There are `Array[T]` and `FixedArray[T]`: + +- `Array[T]` can grow in size, while +- `FixedArray[T]` has a fixed size, thus it needs to be created with initial value. + +``````{warning} +A common pitfall is creating `FixedArray` with the same initial value: + +```{literalinclude} /sources/language/src/builtin/top.mbt +:language: moonbit +:dedent: +:start-after: start array pitfall +:end-before: end array pitfall +``` + +This is because all the cells reference to the same object (the `FixedArray[Int]` in this case). One should use `FixedArray::makei()` instead which creates an object for each index. + +```{literalinclude} /sources/language/src/builtin/top.mbt +:language: moonbit +:dedent: +:start-after: start array pitfall solution +:end-before: end array pitfall solution +``` +`````` + +When the expected type is known, MoonBit can automatically overload array, otherwise +`Array[T]` is created: + +```{literalinclude} /sources/language/src/builtin/top.mbt +:language: moonbit +:start-after: start array 3 +:end-before: end array 3 +``` + +#### ArrayView + +Analogous to `slice` in other languages, the view is a reference to a +specific segment of collections. You can use `data[start:end]` to create a +view of array `data`, referencing elements from `start` to `end` (exclusive). +Both `start` and `end` indices can be omitted. + +```{literalinclude} /sources/language/src/operator/top.mbt +:language: moonbit +:start-after: start view 1 +:end-before: end view 1 +``` + ### Map -MoonBit provides a hash map data structure that preserves insertion orde called `Map` in its standard library. +MoonBit provides a hash map data structure that preserves insertion order called `Map` in its standard library. `Map`s can be created via a convenient literal syntax: ```{literalinclude} /sources/language/src/builtin/top.mbt @@ -294,7 +341,7 @@ The expression `add3(1, 2, 7)` returns `10`. Any expression that evaluates to a ### Labelled arguments -Functions can declare labelled argument with the syntax `label~ : Type`. `label` will also serve as parameter name inside function body: +**Top-level** functions can declare labelled argument with the syntax `label~ : Type`. `label` will also serve as parameter name inside function body: ```{literalinclude} /sources/language/src/functions/top.mbt :language: moonbit @@ -400,7 +447,7 @@ Autofill arguments are very useful for writing debugging and testing utilities. ### Conditional Expressions -A conditional expression consists of a condition, a consequent, and an optional else clause. +A conditional expression consists of a condition, a consequent, and an optional `else` clause or `else if` clause. ```{literalinclude} /sources/language/src/controls/top.mbt :language: moonbit @@ -409,16 +456,7 @@ A conditional expression consists of a condition, a consequent, and an optional :end-before: end conditional expressions 1 ``` -The else clause can also contain another if-else expression: - -```{literalinclude} /sources/language/src/controls/top.mbt -:language: moonbit -:dedent: -:start-after: start conditional expressions 2 -:end-before: end conditional expressions 2 -``` - -Curly brackets are used to group multiple expressions in the consequent or the else clause. +The curly brackets around the consequent are required. Note that a conditional expression always returns a value in MoonBit, and the return values of the consequent and the else clause must be of the same type. Here is an example: @@ -429,6 +467,8 @@ Note that a conditional expression always returns a value in MoonBit, and the re :end-before: end conditional expressions 3 ``` +The `else` clause can only be omitted if the return value has type `Unit`. + ### While loop In MoonBit, `while` loop can be used to execute a block of code repeatedly as long as a condition is true. The condition is evaluated before executing the block of code. The `while` loop is defined using the `while` keyword, followed by a condition and the loop body. The loop body is a sequence of statements. The loop body is executed as long as the condition is true. @@ -568,6 +608,15 @@ MoonBit supports traversing elements of different data structures and sequences `for .. in` loop is translated to the use of `Iter` in MoonBit's standard library. Any type with a method `.iter() : Iter[T]` can be traversed using `for .. in`. For more information of the `Iter` type, see [Iterator](#iterator) below. +`for .. in` loop also supports iterating through a sequence of integers, such as: + +```{literalinclude} /sources/language/src/controls/top.mbt +:language: moonbit +:dedent: +:start-after: start for loop 10 +:end-before: end for loop 10 +``` + In addition to sequences of a single value, MoonBit also supports traversing sequences of two values, such as `Map`, via the `Iter2` type in MoonBit's standard library. Any type with method `.iter2() : Iter2[A, B]` can be traversed using `for .. in` with two loop variables: @@ -951,14 +1000,17 @@ MoonBit supports type alias via the syntax `typealias Name = TargetType`: :end-before: end typealias 1 ``` -unlike all other kinds of type declaration above, type alias does not define a new type, +Unlike all other kinds of type declaration above, type alias does not define a new type, it is merely a type macro that behaves exactly the same as its definition. So for example one cannot define new methods or implement traits for a type alias. +```{tip} Type alias can be used to perform incremental code refactor. + For example, if you want to move a type `T` from `@pkgA` to `@pkgB`, you can leave a type alias `typealias T = @pkgB.T` in `@pkgA`, and **incrementally** port uses of `@pkgA.T` to `@pkgB.T`. The type alias can be removed after all uses of `@pkgA.T` is migrated to `@pkgB.T`. +``` ## Pattern Matching @@ -986,8 +1038,20 @@ There are some other useful constructs in pattern matching. For example, we can :end-before: end pattern 3 ``` +### Array Pattern + +For `Array`, `FixedArray` and `ArrayView`, MoonBit allows using array pattern. + +Array pattern have the following forms: + +- `[]` : matching for an empty data structure +- `[pa, pb, pc]` : matching for known number of elements, 3 in this example +- `[pa, ..]` : matching for known number of elements, followed by unknown number of elements +- `[.., pa]` : matching for known number of elements, preceded by unknown number of elements + ### Range Pattern For builtin integer types and `Char`, MoonBit allows matching whether the value falls in a specific range. + Range patterns have the form `a..`, which can be used to chain regular function calls: + +```{literalinclude} /sources/language/src/operator/top.mbt +:language: moonbit +:dedent: +:start-after: start operator 4 +:end-before: end operator 4 +``` + +### Cascade Operator + +The cascade operator `..` is used to perform a series of mutable operations on +the same value consecutively. The syntax is as follows: + +```{literalinclude} /sources/language/src/operator/top.mbt +:language: moonbit +:dedent: +:start-after: start operator 5 +:end-before: end operator 5 +``` + +`x..f()..g()` is equivalent to `{x.f(); x.g(); x}`. + +Consider the following scenario: for a `StringBuilder` type that has methods +like `write_string`, `write_char`, `write_object`, etc., we often need to perform +a series of operations on the same `StringBuilder` value: + +```{literalinclude} /sources/language/src/operator/top.mbt +:language: moonbit +:dedent: +:start-after: start operator 6 +:end-before: end operator 6 +``` + +To avoid repetitive typing of `builder`, its methods are often designed to +return `self` itself, allowing operations to be chained using the `.` operator. +To distinguish between immutable and mutable operations, in MoonBit, +for all methods that return `Unit`, cascade operator can be used for +consecutive operations without the need to modify the return type of the methods. + +```{literalinclude} /sources/language/src/operator/top.mbt +:language: moonbit +:dedent: +:start-after: start operator 7 +:end-before: end operator 7 +``` + ### TODO syntax The `todo` syntax (`...`) is a special construct used to mark sections of code that are not yet implemented or are placeholders for future functionality. For example: diff --git a/next/language/introduction.md b/next/language/introduction.md index 3269c4ad..a2d8fc6d 100644 --- a/next/language/introduction.md +++ b/next/language/introduction.md @@ -7,46 +7,6 @@ A MoonBit program consists of top-level definitions including: - constant definitions and variable bindings - `init` functions, `main` function and/or `test` blocks. -## Program entrance - -There is a specialized function called `init` function. The `init` function is special in two aspects: - -1. There can be multiple `init` functions in the same package. -2. An `init` function can't be explicitly called or referred to by other functions. Instead, all `init` functions will be implicitly called when initializing a package. Therefore, `init` functions should only consist of statements. - -```{literalinclude} /sources/language/src/main/top.mbt -:language: moonbit -:start-after: start init -:end-before: end init -``` - -For WebAssembly backend, it means that it will be executed **before** the instance is available, meaning that the FFIs that relies on the instance's exportations can not be used at this stage; -for JavaScript backend, it means that it will be executed during the importation stage. - -There is another specialized function called `main` function. The `main` function is the main entrance of the program, and it will be executed after the initialization stage. - -```{literalinclude} /sources/language/src/main/top.mbt -:language: moonbit -:start-after: start main -:end-before: end main -``` - -The previous two code snippets will print the following at runtime: - -```bash -1 -2 -``` - -Only packages that are `main` packages can define such `main` function. Check out [build system tutorial](/toolchain/moon/tutorial) for detail. - -```{literalinclude} /sources/language/src/main/moon.pkg.json -:language: json -:caption: moon.pkg.json -``` - -The two functions above need to drop the parameter list and the return type. - ## Expressions and Statements MoonBit distinguishes between statements and expressions. In a function body, only the last clause should be an expression, which serves as a return value. For example: @@ -61,10 +21,10 @@ Expressions include: - Value literals (e.g. Boolean values, numbers, characters, strings, arrays, tuples, structs) - Arithmetical, logical, or comparison operations -- Accesses to array elements (e.g. `a[0]`) or struct fields (e.g `r.x`) or tuple components (e.g. `t.0`) +- Accesses to array elements (e.g. `a[0]`), struct fields (e.g `r.x`), tuple components (e.g. `t.0`), etc. - Variables and (capitalized) enum constructors - Anonymous local function definitions -- `match` and `if` expressions +- `match`, `if`, `loop` expressions, etc. Statements include: @@ -72,12 +32,16 @@ Statements include: - Local variable bindings - Assignments - `return` statements -- Any expression whose return type is `Unit` +- Any expression whose return type is `Unit`, (e.g. `ignore`) + +A code block can contain multiple statements and one expression, and the value of the expression is the value of the code block. ## Variable Binding A variable can be declared as mutable or immutable using `let mut` or `let`, respectively. A mutable variable can be reassigned to a new value, while an immutable one cannot. +A constant can only be declared at top level and cannot be changed. + ```{literalinclude} /sources/language/src/variable/top.mbt :language: moonbit ``` @@ -88,4 +52,56 @@ Variables, functions should start with lowercase letters `a-z` and can contain l It is recommended to name them with snake_case. Constants, types should start with uppercase letters `A-Z` and can contain letters, numbers, and other non-ascii unicode chars. -It is recommended to name them with PascalCase or SCREAMING_SNAKE_CASE. \ No newline at end of file +It is recommended to name them with PascalCase or SCREAMING_SNAKE_CASE. + +## Program entrance + +### `init` and `main` +There is a specialized function called `init` function. The `init` function is special: + +1. It has no parameter list nor return type. +2. There can be multiple `init` functions in the same package. +3. An `init` function can't be explicitly called or referred to by other functions. +Instead, all `init` functions will be implicitly called when initializing a package. Therefore, `init` functions should only consist of statements. + +```{literalinclude} /sources/language/src/main/top.mbt +:language: moonbit +:start-after: start init +:end-before: end init +``` + +There is another specialized function called `main` function. The `main` function is the main entrance of the program, and it will be executed after the initialization stage. + +Same as the `init` function, it has no parameter list nor return type. + +```{literalinclude} /sources/language/src/main/top.mbt +:language: moonbit +:start-after: start main +:end-before: end main +``` + +The previous two code snippets will print the following at runtime: + +```bash +1 +2 +``` + +Only packages that are `main` packages can define such `main` function. Check out [build system tutorial](/toolchain/moon/tutorial) for detail. + +```{literalinclude} /sources/language/src/main/moon.pkg.json +:language: json +:caption: moon.pkg.json +``` + +### `test` + +There's also a top-level structure called `test` block. A `test` block defines inline tests, such as: + +```{literalinclude} /sources/language/src/test/top.mbt +:language: moonbit +:start-after: start test 1 +:end-before: end test 1 +``` + +The following contents will use `test` block and `main` function to demonstrate the execution result. \ No newline at end of file diff --git a/next/language/methods.md b/next/language/methods.md index e9d9206d..d385b2ef 100644 --- a/next/language/methods.md +++ b/next/language/methods.md @@ -110,84 +110,14 @@ Currently, the following operators can be overloaded: | `_[_]` (get item) | `op_get` | | `_[_] = _` (set item) | `op_set` | | `_[_:_]` (view) | `op_as_view` | - -### Pipe operator - -MoonBit provides a convenient pipe operator `|>`, which can be used to chain regular function calls: - -```{literalinclude} /sources/language/src/operator/top.mbt -:language: moonbit -:dedent: -:start-after: start operator 4 -:end-before: end operator 4 -``` - -### Cascade Operator - -The cascade operator `..` is used to perform a series of mutable operations on -the same value consecutively. The syntax is as follows: - -```{literalinclude} /sources/language/src/operator/top.mbt -:language: moonbit -:dedent: -:start-after: start operator 5 -:end-before: end operator 5 -``` - -`x..f()..g()` is equivalent to `{x.f(); x.g(); x}`. - -Consider the following scenario: for a `StringBuilder` type that has methods -like `write_string`, `write_char`, `write_object`, etc., we often need to perform -a series of operations on the same `StringBuilder` value: - -```{literalinclude} /sources/language/src/operator/top.mbt -:language: moonbit -:dedent: -:start-after: start operator 6 -:end-before: end operator 6 -``` - -To avoid repetitive typing of `builder`, its methods are often designed to -return `self` itself, allowing operations to be chained using the `.` operator. -To distinguish between immutable and mutable operations, in MoonBit, -for all methods that return `Unit`, cascade operator can be used for -consecutive operations without the need to modify the return type of the methods. - -```{literalinclude} /sources/language/src/operator/top.mbt -:language: moonbit -:dedent: -:start-after: start operator 7 -:end-before: end operator 7 -``` - -### Bitwise Operator - -MoonBit supports C-Style bitwise operators. - -| Operator | Perform | -| -------- | ------- | | `&` | `land` | | `\|` | `lor` | | `^` | `lxor` | | `<<` | `op_shl` | | `>>` | `op_shr` | -### View Operator - - - -Analogous to `slice` in other languages, the view is a reference to a -specific segment of collections. You can use `data[start:end]` to create a -view of array `data`, referencing elements from `start` to `end` (exclusive). -Both `start` and `end` indices can be omitted. - -```{literalinclude} /sources/language/src/operator/top.mbt -:language: moonbit -:start-after: start view 1 -:end-before: end view 1 -``` -By implementing `op_as_view` method, you can also create a view for a user-defined type. Here is an example: +By implementing `op_as_view` method, you can create a view for a user-defined type. Here is an example: ```{literalinclude} /sources/language/src/operator/top.mbt :language: moonbit diff --git a/next/sources/language/src/builtin/top.mbt b/next/sources/language/src/builtin/top.mbt index 66b4fa38..be7b7525 100644 --- a/next/sources/language/src/builtin/top.mbt +++ b/next/sources/language/src/builtin/top.mbt @@ -160,6 +160,31 @@ test { } // end array 2 +// start array 3 +let fixed_array_1 : FixedArray[Int] = [1, 2, 3] +let fixed_array_2 = ([1, 2, 3] : FixedArray[Int]) +let array_3 = [1, 2, 3] // Array[Int] +// end array 3 + +// start array pitfall +test { + let two_dimension_array = FixedArray::make(10, FixedArray::make(10, 0)) + two_dimension_array[0][5] = 10 + assert_eq!(two_dimension_array[5][5], 10) +} +// end array pitfall + +// start array pitfall solution +test { + let two_dimension_array = FixedArray::makei( + 10, + fn (_i) { FixedArray::make(10, 0) } + ) + two_dimension_array[0][5] = 10 + assert_eq!(two_dimension_array[5][5], 0) +} +// end array pitfall solution + // start map 1 let map : Map[String, Int] = { "x": 1, "y": 2, "z": 3 } // end map 1 diff --git a/next/sources/language/src/controls/top.mbt b/next/sources/language/src/controls/top.mbt index cbc6a2b1..465b4ced 100644 --- a/next/sources/language/src/controls/top.mbt +++ b/next/sources/language/src/controls/top.mbt @@ -1,28 +1,21 @@ fn a() -> Int { let x = 1 let y = 1 + let z = 1 let expr1 = 1 let expr2 = 1 + let expr3 = 1 // start conditional expressions 1 if x == y { expr1 - } else { + } else if x == z { expr2 + } else { + expr3 } // end conditional expressions 1 } -fn b() -> Unit { - let x = 1 - let y = 1 - let expr1 = () - // start conditional expressions 2 - if x == y { - expr1 - } - // end conditional expressions 2 -} - fn c() -> Unit { let size = 0 // start conditional expressions 3 @@ -211,6 +204,22 @@ test { } // end for loop 9 +// start for loop 10 +test { + let mut i = 0 + for j in 0..<10 { + i += j + } + assert_eq!(i, 45) + + let mut k = 0 + for l in 0..=10 { + k += l + } + assert_eq!(k, 55) +} +// end for loop 10 + fn f() -> Unit { let index = 1 let len = 10 diff --git a/next/sources/language/src/functions/top.mbt b/next/sources/language/src/functions/top.mbt index 4713bb57..f5371c2e 100644 --- a/next/sources/language/src/functions/top.mbt +++ b/next/sources/language/src/functions/top.mbt @@ -6,7 +6,7 @@ fn foo() -> Int { fn bar() -> Int { let x = 1 - // x + 1 // fail + //! x + 1 x + 2 } // end expression @@ -38,7 +38,7 @@ fn local_1() -> Int { fn inc(x) { // named as `inc` x + 1 } - // anonymous, instantly appplied to integer literal 6 + // anonymous, instantly applied to integer literal 6 (fn(x) { x + inc(2) })(6) } diff --git a/next/sources/language/src/test/top.mbt b/next/sources/language/src/test/top.mbt index 4e7d3454..a0a34184 100644 --- a/next/sources/language/src/test/top.mbt +++ b/next/sources/language/src/test/top.mbt @@ -2,6 +2,7 @@ test "test_name" { assert_eq!(1 + 1, 2) assert_eq!(2 + 2, 4) + inspect!([1, 2, 3], content="[1, 2, 3]") } // end test 1 diff --git a/next/sources/language/src/variable/top.mbt b/next/sources/language/src/variable/top.mbt index 0172ee78..93980140 100644 --- a/next/sources/language/src/variable/top.mbt +++ b/next/sources/language/src/variable/top.mbt @@ -1,7 +1,10 @@ let zero = 0 +const ZERO = 0 + fn main { + //! const ZERO = 0 let mut i = 10 i = 20 - println(i + zero) + println(i + zero + ZERO) }