From eb6f5634aed7d019a261785204f6ef1531b49003 Mon Sep 17 00:00:00 2001 From: zihang Date: Fri, 5 Jul 2024 15:26:29 +0800 Subject: [PATCH] fix: use moonbit as the only language tag --- README.md | 190 +++++++++++++++---------------- build-system-tutorial.md | 16 +-- ffi-and-wasm-host.md | 8 +- zh-docs/README.md | 184 +++++++++++++++--------------- zh-docs/build-system-tutorial.md | 16 +-- zh-docs/ffi-and-wasm-host.md | 8 +- 6 files changed, 211 insertions(+), 211 deletions(-) diff --git a/README.md b/README.md index 871b2b78..f4bb1a74 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ A MoonBit program consists of type definitions, function definitions, and variab 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. -```rust live +```moonbit live fn init { print("Hello world!") // OK } @@ -36,7 +36,7 @@ fn init { 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: -```rust live +```moonbit live fn foo() -> Int { let x = 1 x + 1 // OK @@ -81,7 +81,7 @@ Functions take arguments and produce a result. In MoonBit, functions are first-c Functions can be defined as top-level or local. We can use the `fn` keyword to define a top-level function that sums three integers and returns the result, as follows: -```rust +```moonbit fn add3(x: Int, y: Int, z: Int)-> Int { x + y + z } @@ -93,7 +93,7 @@ Note that the arguments and return value of top-level functions require explicit Local functions can be named or anonymous. Type annotations can be omitted for local function definitions: they can be automatically inferred in most cases. For example: -```go live +```moonbit live fn foo() -> Int { fn inc(x) { x + 1 } // named as `inc` fn (x) { x + inc(2) } (6) // anonymous, instantly applied to integer literal 6 @@ -106,7 +106,7 @@ fn init { Functions, whether named or anonymous, are _lexical closures_: any identifiers without a local binding must refer to bindings from a surrounding lexical scope. For example: -```go live +```moonbit live let y = 3 fn foo(x: Int) -> Unit { fn inc() { x + 1 } // OK, will return x + 1 @@ -124,13 +124,13 @@ fn init { A function can be applied to a list of arguments in parentheses: -```go +```moonbit add3(1, 2, 7) ``` This works whether `add3` is a function defined with a name (as in the previous example), or a variable bound to a function value, as shown below: -```go live +```moonbit live fn init { let add3 = fn(x, y, z) { x + y + z } print(add3(1, 2, 7)) @@ -139,7 +139,7 @@ fn init { The expression `add3(1, 2, 7)` returns `10`. Any expression that evaluates to a function value is applicable: -```go live +```moonbit live fn init { let f = fn (x) { x + 1 } let g = fn (x) { x + 2 } @@ -150,7 +150,7 @@ fn init { ### Labelled arguments Functions can declare labelled argument with the syntax `~label : Type`. `label` will also serve as parameter name inside function body: -```rust +```moonbit fn labelled(~arg1 : Int, ~arg2 : Int) -> Int { arg1 + arg2 } @@ -158,7 +158,7 @@ fn labelled(~arg1 : Int, ~arg2 : Int) -> Int { Labelled arguments can be supplied via the syntax `label=arg`. `label=label` can be abbreviated as `~label`: -```rust +```moonbit fn init { let arg1 = 1 println(labelled(arg2=2, ~arg1)) // 3 @@ -170,7 +170,7 @@ Labelled function can be supplied in any order. The evaluation order of argument ### Optional arguments A labelled argument can be made optional by supplying a default expression with the syntax `~label : Type = default_expr`. If this argument is not supplied at call site, the default expression will be used: -```rust live +```moonbit live fn optional(~opt : Int = 42) -> Int { opt } @@ -183,7 +183,7 @@ fn init { The default expression will be evaluated everytime it is used. And the side effect in the default expression, if any, will also be triggered. For example: -```rust live +```moonbit live fn incr(~counter : Ref[Int] = { val: 0 }) -> Ref[Int] { counter.val = counter.val + 1 counter @@ -200,7 +200,7 @@ fn init { If you want to share the result of default expression between different function calls, you can lift the default expression to a toplevel `let` declaration: -```rust live +```moonbit live let default_counter : Ref[Int] = { val: 0 } fn incr(~counter : Ref[Int] = default_counter) -> Int { @@ -216,7 +216,7 @@ fn init { Default expression can depend on the value of previous arguments. For example: -```rust +```moonbit fn sub_array[X](xs : Array[X], ~offset : Int, ~len : Int = xs.length() - offset) -> Array[X] { ... // take a sub array of [xs], starting from [offset] with length [len] } @@ -235,7 +235,7 @@ Now if the argument is not explicitly supplied, MoonBit will automatically fill Currently MoonBit supports two types of autofill arguments, `SourceLoc`, which is the source location of the whole function call, and `ArgsLoc`, which is a array containing the source location of each argument, if any: -```rust +```moonbit fn f(_x : Int, _y : Int, ~loc : SourceLoc = _, ~args_loc : ArgsLoc = _) -> Unit { println("loc of whole function call: \(loc)") println("loc of arguments: \(args_loc)") @@ -256,7 +256,7 @@ Autofill arguments are very useful for writing debugging and testing utilities. A conditional expression consists of a condition, a consequent, and an optional else clause. -```go +```moonbit if x == y { expr1 } else { @@ -270,7 +270,7 @@ if x == y { The else clause can also contain another if-else expression: -```go +```moonbit if x == y { expr1 } else if z == k { @@ -289,7 +289,7 @@ Note that a conditional expression always returns a value in MoonBit, and the re 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. -```rust +```moonbit let mut i = 5 while i > 0 { println(i) @@ -300,7 +300,7 @@ while i > 0 { The loop body supports `break` and `continue`. Using `break` allows you to exit the current loop, while using `continue` skips the remaining part of the current iteration and proceeds to the next iteration. -```rust +```moonbit let mut i = 5 while i > 0 { i = i - 1 @@ -319,7 +319,7 @@ Output: The `while` loop also supports an optional `else` clause. When the loop condition becomes false, the `else` clause will be executed, and then the loop will end. -```rust +```moonbit let mut i = 2 while i > 0 { println(i) @@ -340,7 +340,7 @@ Output: When there is an `else` clause, the `while` loop can also return a value. The return value is the evaluation result of the `else` clause. In this case, if you use `break` to exit the loop, you need to provide a return value after `break`, which should be of the same type as the return value of the `else` clause. -```rust +```moonbit let mut i = 10 let r1 = while i > 0 { @@ -352,7 +352,7 @@ When there is an `else` clause, the `while` loop can also return a value. The re println(r1) //output: 5 ``` -```rust +```moonbit let mut i = 10 let r2 = while i > 0 { @@ -368,7 +368,7 @@ When there is an `else` clause, the `while` loop can also return a value. The re MoonBit also supports C-style For loops. The keyword `for` is followed by variable initialization clauses, loop conditions, and update clauses separated by semicolons. They do not need to be enclosed in parentheses. For example, the code below creates a new variable binding `i`, which has a scope throughout the entire loop and is immutable. This makes it easier to write clear code and reason about it: -```rust +```moonbit for i = 0; i < 5; i = i + 1 { println(i) } @@ -380,7 +380,7 @@ for i = 0; i < 5; i = i + 1 { The variable initialization clause can create multiple bindings: -```rust +```moonbit for i = 0, j = 0; i + j < 100; i = i + 1, j = j + 1 { println(i) } @@ -391,13 +391,13 @@ It should be noted that in the update clause, when there are multiple binding va Variable initialization clauses, loop conditions, and update clauses are all optional. For example, the following two are infinite loops: -```rust +```moonbit for i=1;; i=i+1 { println(i) // loop forever! } ``` -```rust +```moonbit for { println("loop forever!") } @@ -410,7 +410,7 @@ The `continue` statement skips the remaining part of the current iteration of th For example, the following program calculates the sum of even numbers from 1 to 6: -```rust live +```moonbit live fn main { let sum = for i = 1, acc = 0; i <= 6; i = i + 1 { @@ -440,7 +440,7 @@ Functional loop is a powerful feature in MoonBit that enables you to write loops A functional loop consumes arguments and returns a value. It is defined using the `loop` keyword, followed by its arguments and the loop body. The loop body is a sequence of clauses, each of which consists of a pattern and an expression. The clause whose pattern matches the input will be executed, and the loop will return the value of the expression. If no pattern matches, the loop will panic. Use the `continue` keyword with arguments to start the next iteration of the loop. Use the `break` keyword with arguments to return a value from the loop. The `break` keyword can be omitted if the value is the last expression in the loop body. -```rust live +```moonbit live fn sum(xs: List[Int]) -> Int { loop xs, 0 { Nil, acc => break acc // break can be omitted @@ -459,7 +459,7 @@ fn init { MoonBit has a built-in boolean type, which has two values: `true` and `false`. The boolean type is used in conditional expressions and control structures. -```rust +```moonbit let a = true let b = false let c = a && b @@ -485,7 +485,7 @@ To improve readability, you may place underscores in the middle of numeric liter - There is nothing surprising about decimal numbers. -```rust +```moonbit let a = 1234 let b = 1_000_000 + a let large_num = 9_223_372_036_854_775_807L // Integers of the Int64 type must have an 'L' as a suffix @@ -495,7 +495,7 @@ let unsigned_num = 4_294_967_295U // Integers of the UInt type must have an 'U' - A binary number has a leading zero followed by a letter "B", i.e. `0b`/`0B`. Note that the digits after `0b`/`0B` must be `0` or `1`. -```rust +```moonbit let bin = 0b110010 let another_bin = 0B110010 ``` @@ -503,7 +503,7 @@ let another_bin = 0B110010 - An octal number has a leading zero followed by a letter "O", i.e. `0o`/`0O`. Note that the digits after `0o`/`0O` must be in the range from `0` through `7`: -```rust +```moonbit let octal = 0o1234 let another_octal = 0O1234 ``` @@ -511,7 +511,7 @@ let another_octal = 0O1234 - A hexadecimal number has a leading zero followed by a letter "X", i.e. `0x`/`0X`. Note that the digits after the `0x`/`0X` must be in the range `0123456789ABCDEF`. -```rust +```moonbit let hex = 0XA let another_hex = 0xA ``` @@ -520,13 +520,13 @@ let another_hex = 0xA `String` holds a sequence of UTF-16 code units. You can use double quotes to create a string, or use `#|` to write a multi-line string. -```rust +```moonbit let a = "兔rabbit" println(a[0]) // output: 兔 println(a[1]) // output: r ``` -```rust +```moonbit let b = #| Hello #| MoonBit @@ -546,7 +546,7 @@ In double quotes string, a backslash followed by certain special characters form MoonBit supports string interpolation. It enables you to substitute variables within interpolated strings. This feature simplifies the process of constructing dynamic strings by directly embedding variable values into the text. -```swift live +```moonbit live fn init { let x = 42 print("The answer is \(x)") @@ -559,7 +559,7 @@ Variables used for string interpolation must support the `to_string` method. `Char` is an integer representing a Unicode code point. -```rust +```moonbit let a : Char = 'A' let b = '\x41' let c = '🐰' @@ -569,7 +569,7 @@ let c = '🐰' A byte literal in MoonBit is either a single ASCII character or a single escape enclosed in single quotes `'`, and preceded by the character `b`. Byte literals are of type `Byte`. For example: -```rust live +```moonbit live fn init { let b1 : Byte = b'a' println(b1.to_int()) @@ -582,7 +582,7 @@ fn init { A tuple is a collection of finite values constructed using round brackets `()` with the elements separated by commas `,`. The order of elements matters; for example, `(1,true)` and `(true,1)` have different types. Here's an example: -```go live +```moonbit live fn pack(a: Bool, b: Int, c: String, d: Double) -> (Bool, Int, String, Double) { (a, b, c, d) } @@ -595,7 +595,7 @@ fn init { Tuples can be accessed via pattern matching or index: -```go live +```moonbit live fn f(t : (Int, Int)) -> Unit { let (x1, y1) = t // access via pattern matching // access via index @@ -617,13 +617,13 @@ fn init { An array is a finite sequence of values constructed using square brackets `[]`, with elements separated by commas `,`. For example: -```go +```moonbit let numbers = [1, 2, 3, 4] ``` You can use `numbers[x]` to refer to the xth element. The index starts from zero. -```go live +```moonbit live fn init { let numbers = [1, 2, 3, 4] let a = numbers[2] @@ -637,7 +637,7 @@ fn init { 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. -```go live +```moonbit live let zero = 0 fn init { @@ -655,7 +655,7 @@ There are two ways to create new data types: `struct` and `enum`. In MoonBit, structs are similar to tuples, but their fields are indexed by field names. A struct can be constructed using a struct literal, which is composed of a set of labeled values and delimited with curly brackets. The type of a struct literal can be automatically inferred if its fields exactly match the type definition. A field can be accessed using the dot syntax `s.f`. If a field is marked as mutable using the keyword `mut`, it can be assigned a new value. -```rust live +```moonbit live struct User { id: Int name: String @@ -675,7 +675,7 @@ fn init { If you already have some variable like `name` and `email`, it's redundant to repeat those names when constructing a struct: -```go live +```moonbit live fn init{ let name = "john" let email = "john@doe.com" @@ -685,7 +685,7 @@ fn init{ You can use shorthand instead, it behaves exactly the same. -```go live +```moonbit live fn init{ let name = "john" let email = "john@doe.com" @@ -697,7 +697,7 @@ fn init{ It's useful to create a new struct based on an existing one, but with some fields updated. -```rust live +```moonbit live struct User { id: Int name: String @@ -718,7 +718,7 @@ Enum types are similar to algebraic data types in functional languages. Users fa An enum can have a set of cases (constructors). Constructor names must start with capitalized letter. You can use these names to construct corresponding cases of an enum, or checking which branch an enum value belongs to in pattern matching: -```rust live +```moonbit live // An enum type that represents the ordering relation between two values, // with three cases "Smaller", "Greater" and "Equal" enum Relation { @@ -762,7 +762,7 @@ fn init { Enum cases can also carry payload data. Here's an example of defining an integer list type using enum: -```rust live +```moonbit live enum List { Nil // constructor `Cons` carries additional payload: the first element of the list, @@ -809,7 +809,7 @@ fn is_singleton(l: List) -> Bool { #### Constructor with labelled arguments Enum constructors can have labelled argument: -```rust live +```moonbit live enum E { // `x` and `y` are alabelled argument C(~x : Int, ~y : Int) @@ -835,7 +835,7 @@ fn init { ``` It is also possible to access labelled arguments of constructors like accessing struct fields in pattern matching: -```rust live +```moonbit live enum Object { Point(~x : Double, ~y : Double) Circle(~x : Double, ~y : Double, ~radius : Double) @@ -864,7 +864,7 @@ fn init { #### Constructor with mutable fields It is also possible to define mutable fields for constructor. This is especially useful for defining imperative data structures: -```rust live +```moonbit live // A mutable binary search tree with parent pointer enum Tree[X] { Nil @@ -908,7 +908,7 @@ fn Tree::insert[X : Compare](self : Tree[X], x : X, ~parent : Tree[X]) -> Tree[X MoonBit supports a special kind of enum called newtype: -```rust +```moonbit // `UserId` is a fresh new type different from `Int`, and you can define new methods for `UserId`, etc. // But at the same time, the internal representation of `UserId` is exactly the same as `Int` type UserId Int @@ -917,7 +917,7 @@ type UserName String Newtypes are similar to enums with only one constructor (with the same name as the newtype itself). So, you can use the constructor to create values of newtype, or use pattern matching to extract the underlying representation of a newtype: -```rust +```moonbit fn init { let id: UserId = UserId(1) let name: UserName = UserName("John Doe") @@ -930,7 +930,7 @@ fn init { Besides pattern matching, you can also use `.0` to extract the internal representation of newtypes: -```rust +```moonbit fn init { let id: UserId = UserId(1) let uid: Int = id.0 @@ -942,7 +942,7 @@ fn init { We have shown a use case of pattern matching for enums, but pattern matching is not restricted to enums. For example, we can also match expressions against Boolean values, numbers, characters, strings, tuples, arrays, and struct literals. Since there is only one case for those types other than enums, we can pattern match them using `let` binding instead of `match` expressions. Note that the scope of bound variables in `match` is limited to the case where the variable is introduced, while `let` binding will introduce every variable to the current scope. Furthermore, we can use underscores `_` as wildcards for the values we don't care about, use `..` to ignore remaining fields of struct or elements of array. -```go +```moonbit let id = match u { { id: id, name: _, email: _ } => id } @@ -952,7 +952,7 @@ let { id: id, name: _, email: _ } = u let { id: id, ..} = u ``` -```go +```moonbit let ary = [1,2,3,4] let [a, b, ..] = ary // a = 1, b = 2 let [.., a, b] = ary // a = 3, b = 4 @@ -960,7 +960,7 @@ let [.., a, b] = ary // a = 3, b = 4 There are some other useful constructs in pattern matching. For example, we can use `as` to give a name to some pattern, and we can use `|` to match several cases at once. A variable name can only be bound once in a single pattern, and the same set of variables should be bound on both sides of `|` patterns. -```go +```moonbit match expr { Lit(n) as a => ... Add(e1, e2) | Mul(e1, e2) => ... @@ -970,7 +970,7 @@ match expr { ### Map Pattern MoonBit allows convenient matching on map-like data structures: -```rust +```moonbit match map { // matches if any only if "b" exists in `map` { "b": Some(_) } => .. @@ -989,7 +989,7 @@ match map { ## Error Handling The return type of a function can include an error type to indicate that the function might return an error. For example, the following function declaration indicates that the function div might return an error of type String: -```rust +```moonbit fn div(x: Int, y: Int) -> Int!String { if y == 0 { raise "division by zero" @@ -1002,7 +1002,7 @@ The keyword `raise` is used to interrupt the function execution and return an er * Using the `!!` suffix to panic directly in case of an error, for example: -```rust +```moonbit fn div_unsafe(x: Int, y: Int) -> Int { div(x, y)!! // Panic if `div` raised an error } @@ -1010,7 +1010,7 @@ fn div_unsafe(x: Int, y: Int) -> Int { * Using the `!` suffix to rethrow the error directly in case of an error, for example: -```rust +```moonbit fn div_reraise(x: Int, y: Int) -> Int!String { div(x, y)! // Rethrow the error if `div` raised an error } @@ -1018,7 +1018,7 @@ fn div_reraise(x: Int, y: Int) -> Int!String { * Using `try` and `catch` to catch and handle errors, for example: -```rust +```moonbit fn div_with_default(x: Int, y: Int, default: Int) -> Int { try { div(x, y)! @@ -1032,7 +1032,7 @@ Here, `try` is used to call a function that might throw an error, and `catch` is In MoonBit, error types and error handling are second-class citizens, so error types can only appear in the return value of functions and cannot be used as the type of variables. Error handling with the `!` or `!!` suffix can only be used at the function call site and not in other expressions. Valid usage forms include: -```rust +```moonbit f(x)! x.f()! (x |> f)! @@ -1045,7 +1045,7 @@ Additionally, if the return type of a function includes an error type, the funct Generics are supported in top-level function and data type definitions. Type parameters can be introduced within square brackets. We can rewrite the aforementioned data type `List` to add a type parameter `T` to obtain a generic version of lists. We can then define generic functions over lists like `map` and `reduce`. -```go +```moonbit enum List[T] { Nil Cons(T, List[T]) @@ -1073,7 +1073,7 @@ By default, all function definitions and variable bindings are _invisible_ to ot - Struct fields cannot be defined as `pub` within an abstract or private struct since it makes no sense. - Enum constructors do not have individual visibility so you cannot use `pub` or `priv` before them. -```go +```moonbit struct R1 { // abstract data type by default x: Int // implicitly private field pub y: Int // ERROR: `pub` field found in an abstract type! @@ -1113,7 +1113,7 @@ priv enum T3 { // explicitly private enum Another useful feature supported in MoonBit is `pub(readonly)` types, which are inspired by [private types](https://v2.ocaml.org/manual/privatetypes.html) in OCaml. In short, values of `pub(readonly)` types can be destructed by pattern matching and the dot syntax, but cannot be constructed or mutated in other packages. Note that there is no restriction within the same package where `pub(readonly)` types are defined. -```go +```moonbit // Package A pub(readonly) struct RO { field: Int @@ -1137,7 +1137,7 @@ fn init { Access control in MoonBit adheres to the principle that a `pub` type, function, or variable cannot be defined in terms of a private type. This is because the private type may not be accessible everywhere that the `pub` entity is used. MoonBit incorporates sanity checks to prevent the occurrence of use cases that violate this principle. -```go +```moonbit pub struct S { x: T1 // OK y: T2 // OK @@ -1158,7 +1158,7 @@ pub let a: T3 // ERROR: public variable has private type `T3`! MoonBit supports methods in a different way from traditional object-oriented languages. A method in MoonBit is just a toplevel function associated with a type constructor. Methods can be defined using the syntax `fn TypeName::method_name(...) -> ...`: -```rust +```moonbit enum MyList[X] { Nil Cons(X, MyList[X]) @@ -1170,7 +1170,7 @@ fn MyList::concat[X](xs: MyList[MyList[X]]) -> MyList[X] { ... } As a convenient shorthand, when the first parameter of a function is named `self`, MoonBit automatically defines the function as a method of the type of `self`: -```rust +```moonbit fn map[X, Y](self: MyList[X], f: (X) -> Y) -> List[Y] { ... } // equivalent to fn MyList::map[X, Y](xs: MyList[X], f: (X) -> Y) -> List[Y] { ... } @@ -1178,7 +1178,7 @@ fn MyList::map[X, Y](xs: MyList[X], f: (X) -> Y) -> List[Y] { ... } Methods are just regular functions owned by a type constructor. So when there is no ambiguity, methods can be called using regular function call syntax directly: -```rust +```moonbit fn init { let xs: MyList[MyList[_]] = ... let ys = concat(xs) @@ -1187,7 +1187,7 @@ fn init { Unlike regular functions, methods support overloading: different types can define methods of the same name. If there are multiple methods of the same name (but for different types) in scope, one can still call them by explicitly adding a `TypeName::` prefix: -```rust live +```moonbit live struct T1 { x1: Int } fn T1::default() -> { { x1: 0 } } @@ -1203,7 +1203,7 @@ fn init { When the first parameter of a method is also the type it belongs to, methods can be called using dot syntax `x.method(...)`. MoonBit automatically finds the correct method based on the type of `x`, there is no need to write the type name and even the package name of the method: -```rust +```moonbit // a package named @list enum List[X] { ... } fn List::length[X](xs: List[X]) -> Int { ... } @@ -1221,7 +1221,7 @@ fn init { MoonBit supports operator overloading of builtin operators via methods. The method name corresponding to a operator `` is `op_`. For example: -```rust live +```moonbit live struct T { x:Int } derive(Debug) @@ -1239,7 +1239,7 @@ fn init { Another example about `op_get` and `op_set`: -```rust live +```moonbit live struct Coord { mut x: Int mut y: Int @@ -1287,7 +1287,7 @@ Currently, the following operators can be overloaded: ## Pipe operator MoonBit provides a convenient pipe operator `|>`, which can be used to chain regular function calls: -```rust +```moonbit fn init { x |> f // equivalent to f(x) x |> f(y) // equivalent to f(x, y) @@ -1306,7 +1306,7 @@ 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 omited. -```rust +```moonbit fn init { let xs = [0,1,2,3,4,5] let s1 : ArrayView[Int] = xs[2..] @@ -1329,7 +1329,7 @@ fn print_array_view[T : Show](view : ArrayView[T]) -> Unit { By implementing `length` and `op_as_view` method, you can also create a view for a user-defined type. Here is an example: -```rust +```moonbit struct MyList[A] { elems : Array[A] } @@ -1373,7 +1373,7 @@ op_as_view: [1,2) MoonBit features a structural trait system for overloading/ad-hoc polymorphism. Traits declare a list of operations, which must be supplied when a type wants to implement the trait. Traits can be declared as follows: -```rust +```moonbit trait I { method(...) -> ... } @@ -1384,7 +1384,7 @@ In the body of a trait definition, a special type `Self` is used to refer to the To implement a trait, a type must provide all the methods required by the trait. However, there is no need to implement a trait explicitly. Types with the required methods automatically implements a trait. For example, the following trait: -```rust +```moonbit trait Show { to_string(Self) -> String } @@ -1394,7 +1394,7 @@ is automatically implemented by builtin types such as `Int` and `Double`. When declaring a generic function, the type parameters can be annotated with the traits they should implement, allowing the definition of constrained generic functions. For example: -```rust +```moonbit trait Number { op_add(Self, Self) -> Self op_mul(Self, Self) -> Self @@ -1407,7 +1407,7 @@ fn square[N: Number](x: N) -> N { Without the `Number` requirement, the expression `x * x` in `square` will result in a method/operator not found error. Now, the function `square` can be called with any type that implements `Number`, for example: -```rust +```moonbit fn init { debug(square(2)) // 4 debug(square(1.5)) // 2.25 @@ -1430,7 +1430,7 @@ fn op_mul(self: Point, other: Point) -> Point { Methods of a trait can be called directly via `Trait::method`. MoonBit will infer the type of `Self` and check if `Self` indeed implements `Trait`, for example: -```rust live +```moonbit live fn init { println(Show::to_string(42)) debug(Compare::compare(1.0, 2.5)) @@ -1439,7 +1439,7 @@ fn init { MoonBit provides the following useful builtin traits: -```rust +```moonbit trait Eq { op_equal(Self, Self) -> Bool } @@ -1473,7 +1473,7 @@ To make the trait system coherent (i.e. there is a globally unique implementatio However, it is often useful to extend the functionality of an existing type. So MoonBit provides a mechanism called extension method, defined using the syntax `fn Trait::method_name(...) -> ...`. Extension methods extend the functionality of an existing type by implementing a trait. For example, to implement a new trait `ToMyBinaryProtocol` for builtin types, one can (and must) use extension methods: -```rust +```moonbit trait ToMyBinaryProtocol { to_my_binary_protocol(Self, Buffer) -> Unit } @@ -1487,7 +1487,7 @@ When searching for the implementation of a trait, extension methods have a highe To invoke an extension method directly, use the `Trait::method` syntax. -```rust live +```moonbit live trait MyTrait { f(Self) -> Unit } @@ -1506,7 +1506,7 @@ fn init { MoonBit can automatically derive implementations for some builtin traits: -```rust live +```moonbit live struct T { x: Int y: Int @@ -1530,7 +1530,7 @@ into a runtime object via `t as I`. Trait object erases the concrete type of a value, so objects created from different concrete types can be put in the same data structure and handled uniformly: -```rust live +```moonbit live trait Animal { speak(Self) } @@ -1571,7 +1571,7 @@ MoonBit features a convenient `?` operator for error handling. The `?` postfix operator can be applied to expressions of type `Option` or `Result`. When applied to expression `t : Option[T]`, `t?` is equivalent to: -```rust +```moonbit match t { None => { return None } Some(x) => x @@ -1580,7 +1580,7 @@ match t { When applied to expression `t: Result[T, E]`, `t?` is equivalent to: -```rust +```moonbit match t { Err(err) => { return Err(err) } Ok(x) => x @@ -1589,7 +1589,7 @@ match t { The question operator can be used to combine codes that may fail or error elegantly: -```rust +```moonbit fn may_fail() -> Option[Int] { ... } fn f() -> Option[Int] { @@ -1612,7 +1612,7 @@ fn g() -> Result[Int, String] { ## Test Blocks MoonBit provides the test code block for writing test cases. For example: -```rust +```moonbit test "test_name" { @test.eq(1 + 1, 2)! @test.eq(2 + 2, 4)! @@ -1621,7 +1621,7 @@ test "test_name" { A test code block is essentially a function that returns a `Unit` but may throws a `String` on error, or `Unit!String` as one would see in its signature at the position of return type. It is called during the execution of `moon test` and outputs a test report through the build system. The `@test.eq` function is from the standard library; if the assertion fails, it prints an error message and terminates the test. The string `"test_name"` is used to identify the test case and is optional. If it starts with `"panic"`, it indicates that the expected behavior of the test is to trigger a panic, and the test will only pass if the panic is triggered. For example: -```rust +```moonbit test "panic_test" { let _ : Int = Option::None.unwrap() } @@ -1630,7 +1630,7 @@ test "panic_test" { Doc comments are comments prefix with `///` in each line in the leading of toplevel structure like `fn`,`let`,`enum`,`struct`,`type`. The doc comments contains a markdown text and several pragmas. -```rust +```moonbit /// Return a new array with reversed elements. /// /// # Example @@ -1655,7 +1655,7 @@ Pragmas are annotations inside doc comments. They all take the form `/// @word . The category can be an arbitrary identifier. It allows configuration to decide which alerts are enabled or turned into errors. - ```rust + ```moonbit /// @alert deprecated "Use foo2 instead" pub fn foo() -> Unit { ... } diff --git a/build-system-tutorial.md b/build-system-tutorial.md index 3036263a..9e560af6 100644 --- a/build-system-tutorial.md +++ b/build-system-tutorial.md @@ -123,14 +123,14 @@ Our `username/hello` module contains two packages: `lib` and `main`. The `lib` package contains `hello.mbt` and `hello_test.mbt` files: `hello.mbt` - ```rust + ```moonbit pub fn hello() -> String { "Hello, world!" } ``` `hello_test.mbt` - ```rust + ```moonbit test "hello" { if hello() != "Hello, world!" { return Err("hello() != \"Hello, world!\"") @@ -140,7 +140,7 @@ The `lib` package contains `hello.mbt` and `hello_test.mbt` files: The `main` package contains a `main.mbt` file: - ```rust + ```moonbit fn main { println(@lib.hello()) } @@ -197,7 +197,7 @@ Now, you can create new files under `lib/fib`: `a.mbt`: -```rust +```moonbit pub fn fib(n : Int) -> Int { match n { 0 => 0 @@ -209,7 +209,7 @@ pub fn fib(n : Int) -> Int { `b.mbt`: -```rust +```moonbit pub fn fib2(num : Int) -> Int { fn aux(n, acc1, acc2) { match n { @@ -265,7 +265,7 @@ In the `main/moon.pkg.json` file, import the `username/hello/lib/fib` package an This line imports the `fib` package, which is part of the `lib` package in the `hello` module. After doing this, you can use the `fib` package in `main/main.mbt`. Replace the file content of `main/main.mbt` to: -```rust +```moonbit fn main { let a = @my_awesome_fibonacci.fib(10) let b = @my_awesome_fibonacci.fib2(11) @@ -288,7 +288,7 @@ Hello, world! Let's add some tests to verify our fib implementation. Add the following content in `lib/fib/a.mbt`: `lib/fib/a.mbt` -```rust +```moonbit fn assert_eq[T: Show + Eq](lhs: T, rhs: T) -> Unit { if lhs != rhs { abort("assert_eq failed.\n lhs: \(lhs)\n rhs: \(rhs)") @@ -313,7 +313,7 @@ Inline test blocks are discarded in non-test compilation modes (`moon build` and Besides inline tests, MoonBit also supports stand-alone test files. Source files ending in `_test.mbt` are considered stand-alone test files. They will be included in test mode only. You can write inline tests and test utilities in these stand-alone test files. For example, inside the `lib/fib` directory, create a file named `fib_test.mbt` and paste the following code: `lib/fib/fib_test.mbt` -```rust +```moonbit test { assert_eq(fib(1), 1) assert_eq(fib2(2), 1) diff --git a/ffi-and-wasm-host.md b/ffi-and-wasm-host.md index 4684c91e..82024813 100644 --- a/ffi-and-wasm-host.md +++ b/ffi-and-wasm-host.md @@ -10,7 +10,7 @@ You can use foreign function in MoonBit through FFI to interact with the hosting You can declare a foreign reference type like this: -```rust +```moonbit type Canvas_ctx ``` @@ -20,7 +20,7 @@ This will be a type that represents a reference to a foreign object, a `CanvasRe You can declare a foreign function like this: -```rust +```moonbit fn cos(d : Double) -> Double = "Math" "cos" ``` @@ -31,7 +31,7 @@ For Wasm(GC) backend, these two strings are used to identify the specific functi You can also declare inline functions where the function body is replaced with one string. For WasmGC backend, you may declare it as a Wasm function without name (which will be generated afterwards): -```rust +```moonbit extern "wasm" fn abs(d : Double) -> Double = #|(func (param f64) (result f64)) ``` @@ -48,7 +48,7 @@ For multi-backend project, you may implement backend specific code in the files You may also declare a foreign function that will be invoked upon a foreign object by using the foreign reference type like this: -```rust +```moonbit fn begin_path(self: Canvas_ctx) = "canvas" "begin_path" ``` diff --git a/zh-docs/README.md b/zh-docs/README.md index 644cca43..e98520da 100644 --- a/zh-docs/README.md +++ b/zh-docs/README.md @@ -22,7 +22,7 @@ MoonBit 目前处于 Pre-alpha 阶段,是实验性质的。我们期望今年 1. 同一个包中可以有多个 `init` 函数。 2. `init` 函数不能被显式调用或被其他函数引用。相反,在一个包初始化时,所有的 `init` 函数都将被隐式地调用。因此,`init` 函数中只能包含语句。 -```rust live +```moonbit live fn init { print("Hello world!") // OK } @@ -36,7 +36,7 @@ fn init { MoonBit 区分语句和表达式。在一个函数体中,只有最后一句才能写成作为返回值的表达式。例如: -```rust live +```moonbit live fn foo() -> Int { let x = 1 x + 1 // OK @@ -84,7 +84,7 @@ fn init { 我们可以使用 `fn` 关键字定义一个顶层函数, 例如以下函数求三个整数之和并返回结果: -```rust +```moonbit fn add3(x: Int, y: Int, z: Int)-> Int { x + y + z } @@ -96,7 +96,7 @@ fn add3(x: Int, y: Int, z: Int)-> Int { 局部函数使用 `fn` 关键字定义。局部函数可以是命名的或匿名的。在大多数情况下,局部函数的类型注解可以省略,因为编译器可以自动推断。例如: -```rust live +```moonbit live fn foo() -> Int { fn inc(x) { x + 1 } // 命名为 `inc` fn (x) { x + inc(2) } (6) // 匿名,立即应用到整数字面量 6 @@ -110,7 +110,7 @@ fn init { 无论是命名的还是匿名的,函数都是 _词法闭包_:任何没有局部绑定的标识符, 必须引用来自周围词法作用域的绑定: -```rust live +```moonbit live let y = 3 fn foo(x: Int) -> Unit { fn inc() { x + 1 } // OK,返回 x + 1 @@ -128,13 +128,13 @@ fn init { 函数可通过向圆括号内传入参数列表进行调用: -```rust +```moonbit add3(1, 2, 7) ``` 这适用于命名函数(如前面的例子)和绑定到函数值的变量,如下所示: -```rust live +```moonbit live fn init { let add3 = fn(x, y, z) { x + y + z } print(add3(1, 2, 7)) @@ -143,7 +143,7 @@ fn init { 表达式 `add3(1, 2, 7)` 返回 `10`。任何求值为函数值的表达式都可以被调用: -```rust live +```moonbit live fn init { let f = fn (x) { x + 1 } let g = fn (x) { x + 2 } @@ -155,7 +155,7 @@ fn init { 可以用 `~label : Type` 的语法为函数声明带标签的参数。函数体内参数的名字也是 `label`: -```rust +```moonbit fn labelled(~arg1 : Int, ~arg2 : Int) -> Int { arg1 + arg2 } @@ -163,7 +163,7 @@ fn labelled(~arg1 : Int, ~arg2 : Int) -> Int { 调用函数时,可以用 `label=arg` 的语法提供带标签的参数。`label=label` 可以简写成 `~label`: -```rust +```moonbit fn init { let arg1 = 1 println(labelled(arg2=2, ~arg1)) // 3 @@ -176,7 +176,7 @@ fn init { 可选的参数是带有默认值的带标签参数。声明可选的参数的语法是 `~label : Type = default_expr`。调用函数时,如果没有提供这个参数,就会使用默认值作为参数: -```rust live +```moonbit live fn optional(~opt : Int = 42) -> Int { opt } @@ -189,7 +189,7 @@ fn init { 每次使用默认参数调用一个函数时,都会重新求值默认值的表达式,也会被重新触发其中的副作用。例如: -```rust live +```moonbit live fn incr(~counter : Ref[Int] = { val: 0 }) -> Ref[Int] { counter.val = counter.val + 1 counter @@ -206,7 +206,7 @@ fn init { 如果想要在多次不同的函数调用之间共享默认值,可以提前用 `let` 计算并保存默认值: -```rust live +```moonbit live let default_counter : Ref[Int] = { val: 0 } fn incr(~counter : Ref[Int] = default_counter) -> Int { @@ -222,7 +222,7 @@ fn init { 默认值可以依赖于前面的参数,例如: -```rust +```moonbit fn sub_array[X](xs : Array[X], ~offset : Int, ~len : Int = xs.length() - offset) -> Array[X] { ... // 生成 xs 的一个从 offset 开始、长度为 len 的子数组 } @@ -239,7 +239,7 @@ MoonBit 能够自动在每次函数调用时填充某些特定类型的参数, 目前 MoonBit 支持两种类型的自动填充参数。代表整个函数调用在源码中位置的 `SourceLoc` 类型,以及包含每个参数各自的位置的 `ArgsLoc` 类型: -```rust +```moonbit fn f(_x : Int, _y : Int, ~loc : SourceLoc = _, ~args_loc : ArgsLoc = _) -> Unit { println("整个函数调用的位置:\(loc)") println("各个参数的位置:\(args_loc)") @@ -260,7 +260,7 @@ fn init { 条件表达式由条件、结果和一个可选的 `else` 子句组成。 -```rust +```moonbit if x == y { expr1 } else { @@ -274,7 +274,7 @@ if x == y { `else` 子句也可以包含另一个 `if-else` 表达式: -```rust +```moonbit if x == y { expr1 } else if z == k { @@ -291,7 +291,7 @@ if x == y { MoonBit中支持`while`循环。`while`后的循环条件会在循环体之前执行,当循环条件为真时, 执行循环体: -```rust +```moonbit let mut i = 5 while i > 0 { println(i) @@ -302,7 +302,7 @@ while i > 0 { 循环体内支持`break`和`continue`。使用`break`能够跳出当前循环;使用`continue`跳过本次循环的剩余部分,提前进入下一次循环。 -```rust +```moonbit let mut i = 5 while i > 0 { i = i - 1 @@ -321,7 +321,7 @@ while i > 0 { `while` 循环也支持可选的`else`子句。当循环条件转变为假时,将会执行`else`子句,然后循环结束。 -```rust +```moonbit let mut i = 2 while i > 0 { println(i) @@ -342,7 +342,7 @@ while i > 0 { 当存在 `else` 子句时,`while` 循环也可以返回一个值,返回值是 `else` 子句语句块的求值结果。此时如果使用`break`跳出循环,需要在`break`后提供一个返回值,类型与`else`子句的返回值类型一致: -```rust +```moonbit let mut i = 10 let r1 = while i > 0 { @@ -354,7 +354,7 @@ while i > 0 { println(r1) //output: 5 ``` -```rust +```moonbit let mut i = 10 let r2 = while i > 0 { @@ -370,7 +370,7 @@ while i > 0 { MoonBit 也支持 C 风格的 For 循环。关键字`for`后依次跟随以分号间隔的变量初始化子句、循环条件和更新子句。三者不需要使用圆括号包裹。 例如下面的代码创建了一个新的变量绑定`i`, 它的作用域在整个循环中,且是不可变的。这更利于编写清晰的代码和推理: -```rust +```moonbit for i = 0; i < 5; i = i + 1 { println(i) } @@ -382,7 +382,7 @@ for i = 0; i < 5; i = i + 1 { 变量初始化子句中可以创建多个绑定: -```rust +```moonbit for i = 0, j = 0; i + j < 100; i = i + 1, j = j + 1 { println(i) } @@ -394,13 +394,13 @@ for i = 0, j = 0; i + j < 100; i = i + 1, j = j + 1 { 变量初始化子句、循环条件和更新子句都是可选的。例如下面两个无限循环: -```rust +```moonbit for i=1;; i=i+1 { println(i) // loop forever! } ``` -```rust +```moonbit for { println("loop forever!") } @@ -414,7 +414,7 @@ for { 例如,下面的程序计算数字1到6中的偶数的和: -```rust live +```moonbit live fn main { let sum = for i = 1, acc = 0; i <= 6; i = i + 1 { @@ -448,7 +448,7 @@ even: 6 可以使用 `continue` 关键字和参数开始下一次循环迭代,使用 `break` 关键字和参数来从循环中返回一个值。 如果值是循环体中的最后一个表达式,则可以省略 `break` 关键字。 -```rust live +```moonbit live fn sum(xs: List[Int]) -> Int { loop xs, 0 { Nil, acc => break acc // break 可以省略 @@ -467,7 +467,7 @@ fn init { MoonBit 内置了布尔类型,它有两个值:`true` 和 `false`。布尔类型用于条件表达式和控制结构。 -```rust +```moonbit let a = true let b = false let c = a && b @@ -494,7 +494,7 @@ MoonBit 支持的数字字面量,包括十进制、二进制、八进制和十 - 十进制数和往常一样。 -```rust +```moonbit let a = 1234 let b = 1_000_000 + a let large_num = 9_223_372_036_854_775_807L // Int64 类型的整数必须后缀“L” @@ -504,7 +504,7 @@ let unsigned_num = 4_294_967_295U // UInt 类型的整数必须有后缀”U“ - 八进制数的前缀是 0 后接字母 O,也就是 `0o` 或 `0O`。注意在 `0o` 或 `0O` 之后出现的数字只能在 `0` 到 `7` 之间。 -```rust +```moonbit let octal = 0o1234 let another_octal = 0O1234 ``` @@ -512,7 +512,7 @@ let another_octal = 0O1234 - 十六进制数的前缀是 0 后接字母 X,也就是 `0x` 或 `0X`。注意在 `0x` 或 `0X` 之后出现的数字只能是 `0123456789ABCDEF` 之一。 -```rust +```moonbit let hex = 0XA let another_hex = 0xA ``` @@ -521,13 +521,13 @@ let another_hex = 0xA 字符串`String`内部保存了 UTF-16 编码单元序列。可以使用双引号来表示一个字符串,或者通过`#|`来书写多行字符串。 -```rust +```moonbit let a = "兔rabbit" println(a[0]) // output: 兔 println(a[1]) // output: r ``` -```rust +```moonbit let b = #| Hello #| MoonBit @@ -547,7 +547,7 @@ let b = MoonBit 支持字符串插值,它可以把字符串中内插的变量替换为变量具体的值。 这个特性能够简化动态拼接字符串的过程。 -```rust live +```moonbit live fn init { let x = 42 print("The answer is \(x)") @@ -560,7 +560,7 @@ fn init { 字符`Char`是表示一个 Unicode 字符的整数。 -```rust +```moonbit let a : Char = 'A' let b = '\x41' let c = '🐰' @@ -570,7 +570,7 @@ let c = '🐰' 在 MoonBit 中,字节字面量可以是一个 ASCII 字符或一个转义序列,它们被单引号`'`包围,并且前面有字符`b`。字节字面量的类型是 Byte。例如: -```rust live +```moonbit live fn init { let b1 : Byte = b'a' println(b1.to_int()) @@ -584,7 +584,7 @@ fn init { 元组是一个有限值的有序集合,使用圆括号 `()` 构造,其中的元素由逗号 `,` 分隔。 元素的顺序很重要,例如 `(1, true)` 和 `(true, 1)` 是不同的类型。以下是一个例子: -```rust live +```moonbit live fn pack(a: Bool, b: Int, c: String, d: Double) -> (Bool, Int, String, Double) { (a, b, c, d) } @@ -597,7 +597,7 @@ fn init { 可以通过模式匹配或索引来访问元组: -```rust live +```moonbit live fn f(t : (Int, Int)) -> Unit { let (x1, y1) = t // 通过模式匹配访问 // 通过索引访问 @@ -619,13 +619,13 @@ fn init { 数组是由方括号 `[]` 构造的有限值序列,其中元素由逗号 `,` 分隔。例如: -```rust +```moonbit let numbers = [1, 2, 3, 4] ``` 可以用 `numbers[x]` 来引用第 `x` 个元素。索引从零开始。 -```rust live +```moonbit live fn init { let numbers = [1, 2, 3, 4] let a = numbers[2] @@ -640,7 +640,7 @@ fn init { 变量可以通过 `let mut` 或 `let` 分别声明为可变或不可变。 可变变量可以重新赋值,不可变变量则不能。 -```rust live +```moonbit live let zero = 0 fn init { @@ -662,7 +662,7 @@ fn init { 使用点语法 `s.f` 可以访问结构体字段。 如果一个字段使用关键字 `mut` 标记为可变,那么可以给它赋予新的值。 -```rust live +```moonbit live struct User { id: Int name: String @@ -683,7 +683,7 @@ fn init { 如果已经有和结构体的字段同名的变量,并且想使用这些变量作为结构体同名字段的值, 那么创建结构体时,可以只写字段名,不需要把同一个名字重复两次。例如: -```rust live +```moonbit live fn init{ let name = "john" let email = "john@doe.com" @@ -696,7 +696,7 @@ fn init{ 如果想要基于现有的结构体来创建新的结构体,只需修改现有结构体的一部分字段,其他字段的值保持不变, 可以使用结构体更新语法: -```rust live +```moonbit live struct User { id: Int name: String @@ -719,7 +719,7 @@ fn init { 枚举由一组分支(构造器)组成,每个分支都有一个名字(必须以大写字母开头),可以用这个名字来构造对应分支的值, 或者在模式匹配中使用这个名字来判断某个枚举值属于哪个分支: -```rust live +```moonbit live // 一个表示两个值之间的有序关系的枚举类型,有 “小于”、“大于”、“等于” 三个分支 enum Relation { Smaller @@ -761,7 +761,7 @@ fn init { 枚举的分支还可以携带额外的数据。下面是用枚举定义整数列表类型的一个例子: -```rust live +```moonbit live enum List { Nil // 构造器 `Cons` 携带了额外的数据:列表的第一个元素,和列表剩余的部分 @@ -807,7 +807,7 @@ fn is_singleton(l: List) -> Bool { 枚举构造器可以有带标签的参数: -```rust live +```moonbit live enum E { // `x` 和 `y` 是带标签的参数 C(~x : Int, ~y : Int) @@ -834,7 +834,7 @@ fn init { 在模式匹配中,还可以像访问结构体的字段一样直访问取构造器的带标签参数: -```rust live +```moonbit live enum Object { Point(~x : Double, ~y : Double) Circle(~x : Double, ~y : Double, ~radius : Double) @@ -865,7 +865,7 @@ fn init { MoonBit 支持给构造器声明可变的字段。这对实现可变数据结构非常有用: -```rust live +```moonbit live // 一个带父节点指针的可变二叉搜索树的类型 enum Tree[X] { Nil @@ -909,7 +909,7 @@ fn Tree::insert[X : Compare](self : Tree[X], x : X, ~parent : Tree[X]) -> Tree[X MoonBit 支持一种特殊的枚举类型,称为新类型(newtype): -```rust +```moonbit // `UserId` 是一个全新的类型,而且用户可以给 `UserId` 定义新的方法等 // 但与此同时,`UserId` 的内部表示和 `Int` 是完全一致的 type UserId Int @@ -919,7 +919,7 @@ type UserName String 新类型和只有一个构造器(与类型同名)的枚举类型非常相似。 因此,可以使用构造器来创建新类型的值、使用模式匹配来提取新类型的内部表示: -```rust +```moonbit fn init { let id: UserId = UserId(1) let name: UserName = UserName("John Doe") @@ -932,7 +932,7 @@ fn init { 除了模式匹配,还可以使用 `.0` 提取新类型的内部表示: -```rust +```moonbit fn init { let id: UserId = UserId(1) let uid: Int = id.0 @@ -948,7 +948,7 @@ fn init { 需要注意的是,在 `match` 中绑定的变量的作用域仅限于引入该变量的情况分支,而 `let` 绑定会将每个变量都引入到当前作用域。此外,我们可以使用下划线 `_` 作为我们不关心的值的通配符。 -```rust +```moonbit let id = match u { { id: id, name: _, email: _ } => id } @@ -959,7 +959,7 @@ let { id: id, name: _, email: _ } = u 模式匹配还有一些有用的构造。例如,我们可以使用 `as` 为某个模式指定一个名称, 并且可以使用 `|` 同时匹配多个情况。 -```rust +```moonbit match expr { Lit(n) as a => ... Add(e1, e2) | Mul(e1, e2) => ... @@ -971,7 +971,7 @@ match expr { MoonBit 允许模式匹配字典等具有键值对结构的数据结构: -```rust +```moonbit match map { // 匹配键 "b" 存在的情况 { "b": Some(_) } => .. @@ -991,7 +991,7 @@ match map { 函数的返回值类型中可以包含错误类型,用于表示函数可能返回的错误。比如如下函数声明表示函数 `div` 可能返回一个字符串类型的错误: -```rust +```moonbit fn div(x: Int, y: Int) -> Int!String { if y == 0 { raise "division by zero" @@ -1003,21 +1003,21 @@ fn div(x: Int, y: Int) -> Int!String { 其中 `raise` 关键字用于中断函数的执行并返回一个错误。函数的错误处理有以下三种方式: * 使用 `!!` 后缀来在发生错误的情况下直接 panic,比如 -```rust +```moonbit fn div_unsafe(x: Int, y: Int) -> Int { div(x, y)!! // 直接 panic } ``` * 使用 `!` 后缀来在发生错误的情况下将错误直接重新抛出,比如 -```rust +```moonbit fn div_reraise(x: Int, y: Int) -> Int!String { div(x, y)! // 直接重新抛出错误 } ``` * 使用 `try` 和 `catch` 对错误进行捕获并处理,比如 -```rust +```moonbit fn div_with_default(x: Int, y: Int, default: Int) -> Int { try { div(x, y)! @@ -1029,7 +1029,7 @@ fn div_with_default(x: Int, y: Int, default: Int) -> Int { 其中 `try` 用于调用可能会抛出错误的函数,`catch` 用于对捕获的错误进行模式匹配并处理,如果没有捕获到错误则不会执行 `catch` 语句块。 在 MoonBit 中,错误类型和错误处理属于二等公民,因此错误类型只能出现在函数的返回值中,而不能作为变量的类型。使用后缀表达式 `!` 或 `!!` 进行的错误处理也只能在函数调用处进行,而不能在其他表达式中使用,合法的使用形式包括: -```rust +```moonbit f(x)! x.f()! (x |> f)! @@ -1044,7 +1044,7 @@ x.f()! 我们可以重写前面提到的数据类型 `List`,添加类型参数 `T`,以获得一个泛型版本的列表。 然后,我们可以定义泛型函数 `map` 和 `reduce`,用于对列表进行操作。 -```rust +```moonbit enum List[T] { Nil Cons(T, List[T]) @@ -1078,7 +1078,7 @@ fn reduce[S, T](self: List[S], op: (T, S) -> T, init: T) -> T { - 在抽象或私有结构体内,所有字段都不能被定义为 `pub`,因为这样没有意义。 - 枚举类型的构造器没有单独的可见性,所以不能在它们前面使用 `pub` 或 `priv` -```rust +```moonbit struct R1 { // 默认为抽象数据类型 x: Int // 隐式的私有字段 pub y: Int // ERROR: 在抽象类型中找到了 `pub` 字段! @@ -1118,7 +1118,7 @@ priv enum T3 { // 显式的私有枚举 MoonBit 中另一个有用的特性是 `pub(readonly)` 类型,其受到了 OCaml [private types](https://v2.ocaml.org/manual/privatetypes.html)的启发。简而言之,`pub(readonly)` 类型的值可以使用模式匹配或点语法析构,但在其他包中,不能被构造或改变。注意到在 `pub(readonly)` 类型定义的同一个包中,它没有任何限制。 -```rust +```moonbit // Package A pub(readonly) struct RO { field: Int @@ -1144,7 +1144,7 @@ MoonBit 中的访问控制遵循这样一个原则:`pub` 类型、函数或变 这是因为私有类型可能不是在使用 `pub` 实体的所有地方都可以被访问。 MoonBit 内建了一些检查,以防止违反这一原则的用例。 -```rust +```moonbit pub struct s { x: T1 // OK y: T2 // OK @@ -1167,7 +1167,7 @@ MoonBit 支持与传统面向对象语言不同的方法(method)。 某个类型的方法就是与该类型关联的普通函数。 可以使用 `fn TypeName::method_name(...) -> ...` 的语法来为类型 `TypeName` 声明方法: -```rust +```moonbit enum MyList[X] { Nil Cons(X, MyList[X]) @@ -1179,7 +1179,7 @@ fn MyList::concat[X](xs: MyList[MyList[X]]) -> MyList[X] { ... } 如果一个函数的第一个参数名为 `self`,那么 MoonBit 会自动将这个函数定义为 `self` 的类型上的方法: -```rust +```moonbit fn map[X, Y](self: MyList[X], f: (X) -> Y) -> List[Y] { ... } // 等价于 fn MyList::map[X, Y](xs: MyList[X], f: (X) -> Y) -> List[Y] { ... } @@ -1187,7 +1187,7 @@ fn MyList::map[X, Y](xs: MyList[X], f: (X) -> Y) -> List[Y] { ... } 方法就是某个类型所拥有的普通函数。所以,在没有歧义时,它们也可以像普通函数一样调用: -```rust +```moonbit fn init { let xs: MyList[MyList[_]] = ... let ys = concat(xs) @@ -1197,7 +1197,7 @@ fn init { 但和普通函数不同,方法支持重载。不同的类型可以有同名的方法。 如果当前作用域内有多个同名方法,依然可以通过加上 `TypeName::` 的前缀来显式地调用一个方法: -```rust live +```moonbit live struct T1 { x1: Int } fn T1::default() -> { { x1: 0 } } @@ -1215,7 +1215,7 @@ fn init { MoonBit 支持通过方法重载内置运算符。与运算符 `` 相对应的方法名是 `op_`。例如: -```rust live +```moonbit live struct T { x:Int } derive(Debug) @@ -1233,7 +1233,7 @@ fn init { 另一个例子(关于`op_get`和`op_set`): -```rust live +```moonbit live struct Coord { mut x: Int mut y: Int @@ -1282,7 +1282,7 @@ fn init { MoonBit 提供了便利的管道运算符 `|>`,可以用于链式调用普通函数: -```rust +```moonbit fn init { x |> f // 等价于 f(x) x |> f(y) // 等价于 f(x, y) @@ -1298,7 +1298,7 @@ fn init { 类似于其他语言的“切片”,视图能够引用数组等数据类型中的片段。可以使用`data[start..end]`的方式创建一个关于数组`data`的视图,这个视图引用了从下标`start`开始到`end`(不包含`end`)的元素。`start`和`end`也可以省略: -```rust +```moonbit fn init { let xs = [0,1,2,3,4,5] let s1 : ArrayView[Int] = xs[2..] @@ -1321,7 +1321,7 @@ fn print_array_view[T : Show](view : ArrayView[T]) -> Unit { 要为自定义数据类型添加视图支持,需要为它实现`length`和`op_as_view`方法。下面是一个例子: -```rust +```moonbit struct MyList[A] { elems : Array[A] } @@ -1365,7 +1365,7 @@ op_as_view: [1,2) MoonBit 具有用于重载/特设多态(ad-hoc polymorphism)的结构接口系统。 接口描述了满足该接口的类型需要支持哪些操作。接口的声明方式如下: -```rust +```moonbit trait I { f(Self, ...) -> ... } @@ -1375,7 +1375,7 @@ trait I { 一个类型要实现某个接口,就要满足该接口中所有的方法。例如,下面的接口描述了一个能够比较元素是否相等的类型需要满足的方法: -```rust +```moonbit trait Eq { op_equal(Self, Self) -> Bool } @@ -1383,7 +1383,7 @@ trait Eq { 接口无需显式实现,具有所需方法的类型会自动实现接口。考虑以下接口: -```rust +```moonbit trait Show { to_string(Self) -> String } @@ -1394,7 +1394,7 @@ trait Show { 在声明泛型函数时,类型参数可以用它们应该实现的接口作为注解。 如此便能定义只对某些类型可用的泛型函数。例如: -```rust +```moonbit trait Number { op_add(Self, Self) -> Self op_mul(Self, Self) -> Self @@ -1408,7 +1408,7 @@ fn square[N: Number](x: N) -> N { 如果没有 `Number` 的要求,`square` 中的表达式 `x * x` 会导致出现找不到方法/运算符的错误。 现在,函数 `square` 可以与任何实现了 `Number` 接口的类型一起使用,例如: -```rust live +```moonbit live fn init { debug(square(2)) // 4 debug(square(1.5)) // 2.25 @@ -1432,7 +1432,7 @@ fn op_mul(self: Point, other: Point) -> Point { 接口中的方法可以用 `Trait::method` 的语法来直接调用。MoonBit 会推导 `Self` 的具体类型, 并检查 `Self` 是否实现了 `Trait`: -```rust live +```moonbit live fn init { println(Show::to_string(42)) debug(Compare::compare(1.0, 2.5)) @@ -1441,7 +1441,7 @@ fn init { Moonbit 提供了以下实用的内建接口: -```rust +```moonbit trait Eq { op_equal(Self, Self) -> Bool } @@ -1480,7 +1480,7 @@ trait Debug { 它们通过实现接口来拓展现有类型的功能。例如,假设要为内建类型实现一个新的接口 `ToMyBinaryProtocol`,就可以(且必须)使用拓展方法: -```rust +```moonbit trait ToMyBinaryProtocol { to_my_binary_protocol(Self, Buffer) -> Unit } @@ -1499,7 +1499,7 @@ fn ToMyBinaryProtocol::to_my_binary_protocol(x: String, b: Buffer) -> Unit { ... 如果需要直接调用一个拓展方法,可以使用 `Trait::method` 语法。例如: -```rust live +```moonbit live trait MyTrait { f(Self) -> Unit } @@ -1517,7 +1517,7 @@ fn init { Moonbit 可以自动生成一些内建接口的实现: -```rust live +```moonbit live struct T { x: Int y: Int @@ -1542,7 +1542,7 @@ MoonBit 通过接口对象的形式来支持运行时多态。 接口对象擦除了值的具体类型,所以从不同的具体类型所创建的接口对象, 可以被封装在同一个数据结构里,统一进行处理: -```rust live +```moonbit live trait Animal { speak(Self) } @@ -1584,7 +1584,7 @@ MoonBit 提供一个便捷的 `?` 操作符,用于错误处理。 `?` 是一个后缀运算符。它可以作用于类型为 `Option` 或 `Result` 的表达式。 被应用在表达式 `t : Option[T]` 上时,`t?` 等价于: -```rust +```moonbit match t { None => { return None } Some(x) => x @@ -1593,7 +1593,7 @@ match t { 被应用在表达式 `t : Result[T, E]` 上时,`t?` 等价于: -```rust +```moonbit match t { Err(err) => { return Err(err) } Ok(x) => x @@ -1602,7 +1602,7 @@ match t { 问号操作符可以用于优雅地组合多段可能失败或产生错误的程序: -```rust +```moonbit fn may_fail() -> Option[Int] { ... } fn f() -> Option[Int] { @@ -1625,7 +1625,7 @@ fn g() -> Result[Int, String] { ## 测试块 MoonBit 提供了 `test` 代码块,用于编写测试用例,比如 -```rust +```moonbit test "test_name" { @test.eq(1 + 1, 2)! @test.eq(2 + 2, 4)! @@ -1633,7 +1633,7 @@ test "test_name" { ``` `test` 代码块实际上是一个返回 `Unit` ,但是可能抛出 `String` 类型错误的函数(函数签名中记为 `Unit!String` )。它会在执行 `moon test` 的过程中被调用,并通过构建系统输出测试报告。其中 `@test.eq` 是一个标准库中的函数,如果断言失败,会打印错误信息并终止测试。字符串 `"test_name"` 用于标识测试用例,是可选项,当其以 `"panic"` 开头时,表示该测试的期望行为是触发 panic,只有在 panic 被触发的情况下才能通过测试,比如: -```rust +```moonbit test "panic_test" { let _ : Int = Option::None.unwrap() } @@ -1643,7 +1643,7 @@ test "panic_test" { 文档注释是以 `///` 开头的注释,出现在顶层结构(如 `fn`、`let`、`enum`、`struct`、`type`)的每一行前面。文档注释内包含 Markdown 文本和任意个注解。 -```rust +```moonbit /// Return a new array with reversed elements. /// /// # Example @@ -1668,7 +1668,7 @@ fn reverse[T](xs : Array[T]) -> Array[T] { `category`表示`@alert`的类别,它可以是任意标识符。可以通过配置来决定哪些`alert`是启用的或者报告为错误。 - ```rust + ```moonbit /// ... /// @alert deprecated "Use foo2 instead" pub fn foo() -> Unit { ... } diff --git a/zh-docs/build-system-tutorial.md b/zh-docs/build-system-tutorial.md index 870e9607..cb00932b 100644 --- a/zh-docs/build-system-tutorial.md +++ b/zh-docs/build-system-tutorial.md @@ -119,14 +119,14 @@ my-project - `lib` 包含 `hello.mbt` 文件与 `hello_test.mbt` 文件: `hello.mbt` - ```rust + ```moonbit pub fn hello() -> String { "Hello, world!" } ``` `hello_test.mbt` - ```rust + ```moonbit test "hello" { if hello() != "Hello, world!" { return Err("hello() != \"Hello, world!\"") @@ -136,7 +136,7 @@ my-project - `main` 包含一个 `main.mbt` 文件: - ```rust + ```moonbit fn main { println(@lib.hello()) } @@ -193,7 +193,7 @@ mkdir lib/fib `a.mbt`: -```rust +```moonbit pub fn fib(n : Int) -> Int { match n { 0 => 0 @@ -205,7 +205,7 @@ pub fn fib(n : Int) -> Int { `b.mbt`: -```rust +```moonbit pub fn fib2(num : Int) -> Int { fn aux(n, acc1, acc2) { match n { @@ -261,7 +261,7 @@ my-project 这行代码导入了 `fib` 包,它是 `hello` 模块中 `lib` 包的一部分。完成后,你可以在 `main/main.mbt` 中使用 `fib` 包。将 `main/main.mbt` 文件的内容替换为: -```rust +```moonbit fn main { let a = @my_awesome_fibonacci.fib(10) let b = @my_awesome_fibonacci.fib2(11) @@ -285,7 +285,7 @@ Hello, world! `lib/fib/a.mbt` -```rust +```moonbit fn assert_eq[T: Show + Eq](lhs: T, rhs: T) -> Unit { if lhs != rhs { abort("assert_eq failed.\n lhs: \(lhs)\n rhs: \(rhs)") @@ -311,7 +311,7 @@ test { `lib/fib/fib_test.mbt`: -```rust +```moonbit test { assert_eq(fib(1), 1) assert_eq(fib2(2), 1) diff --git a/zh-docs/ffi-and-wasm-host.md b/zh-docs/ffi-and-wasm-host.md index 91156b4c..7d5782a7 100644 --- a/zh-docs/ffi-and-wasm-host.md +++ b/zh-docs/ffi-and-wasm-host.md @@ -10,7 +10,7 @@ 你可以定义一个这样的外部引用类型: -```rust +```moonbit type Canvas_ctx ``` @@ -20,7 +20,7 @@ type Canvas_ctx 你可以像这样定义一个外部函数: -```rust +```moonbit fn cos(d : Double) -> Double = "Math" "cos" ``` @@ -31,7 +31,7 @@ fn cos(d : Double) -> Double = "Math" "cos" 你也可以定义内联函数,函数体是一个字符串。 对于WasmGC后端,你可以以一个不含名称的Wasm函数定义它(名称将会在之后自动生成): -```rust +```moonbit extern "wasm" fn abs(d : Double) -> Double = #|(func (param f64) (result f64)) ``` @@ -48,7 +48,7 @@ extern "js" fn abs(d : Double) -> Double = 你也可以定义一个使用外部引用类型的外部函数,就像这样: -```rust +```moonbit fn begin_path(self: Canvas_ctx) = "canvas" "begin_path" ```