Skip to content

Commit

Permalink
doc: improvements
Browse files Browse the repository at this point in the history
- Reorganize introduction and introduce test block
- Separate operator introduction and operator overloading
- Add missing array pattern
- Fix if introduction
- Add missing for..in loop a..<b a..=b
  • Loading branch information
peter-jerry-ye committed Dec 9, 2024
1 parent 39bffd1 commit 5a69042
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 144 deletions.
5 changes: 5 additions & 0 deletions next/language/ffi-and-wasm-host.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
141 changes: 127 additions & 14 deletions next/language/fundamentals.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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:

Expand All @@ -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.
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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..<b` or `a..=b`, where `..<` means the upper bound is exclusive, and `..=` means inclusive upper bound.
`a` and `b` can be one of:

Expand Down Expand Up @@ -1043,6 +1107,55 @@ Generics are supported in top-level function and data type definitions. Type par

## Special Syntax

### 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
```

### 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:
Expand Down
104 changes: 60 additions & 44 deletions next/language/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -61,23 +21,27 @@ 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:

- Named local function definitions
- 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
```
Expand All @@ -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.
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.
Loading

0 comments on commit 5a69042

Please sign in to comment.