Skip to content

Commit

Permalink
Merge pull request #272 from notch1p/refining
Browse files Browse the repository at this point in the history
README.md: change `!!` for `?` in section Error Handling
  • Loading branch information
Yu-zh authored Aug 7, 2024
2 parents 221d9b8 + 67263cb commit 1254ed5
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 122 deletions.
121 changes: 61 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ MoonBit is an end-to-end programming language toolchain for cloud and edge compu

## Status and aimed timeline

It is currently alpha, experimental. We expect MoonBit to reach *beta-preview* in 02/2024 and *beta* in 06/2024.
It is currently alpha, experimental. We expect MoonBit to reach _beta-preview_ in 02/2024 and _beta_ in 06/2024.

When MoonBit reaches beta, it means any backwards-incompatible changes will be seriously evaluated and MoonBit *can* be used in production(very rare compiler bugs). MoonBit is developed by a talented full time team who had extensive experience in building language toolchains, so we will grow much faster than the typical language ecosystem, you won't wait long to use MoonBit in your production.
When MoonBit reaches beta, it means any backwards-incompatible changes will be seriously evaluated and MoonBit _can_ be used in production(very rare compiler bugs). MoonBit is developed by a talented full time team who had extensive experience in building language toolchains, so we will grow much faster than the typical language ecosystem, you won't wait long to use MoonBit in your production.

## Main advantages

Expand All @@ -28,13 +28,13 @@ There is a specialized function called `init` function. The `init` function is s

```moonbit live
fn init {
print("Hello world!") // OK
println("Hello world!") // OK
}
fn init {
let x = 1
// x // fail
print(x) // success
println(x) // success
}
```

Expand Down Expand Up @@ -63,8 +63,8 @@ fn bar() -> Int {
}
fn init {
print(foo())
print(bar())
println(foo())
println(bar())
}
```

Expand Down Expand Up @@ -112,19 +112,19 @@ fn foo() -> Int {
}
fn init {
print(foo())
println(foo())
}
```

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:
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:

```moonbit live
let y = 3
fn foo(x: Int) -> Unit {
fn inc() { x + 1 } // OK, will return x + 1
fn four() { y + 1 } // Ok, will return 4
print(inc())
print(four())
println(inc())
println(four())
}
fn init {
Expand All @@ -145,7 +145,7 @@ This works whether `add3` is a function defined with a name (as in the previous
```moonbit live
fn init {
let add3 = fn(x, y, z) { x + y + z }
print(add3(1, 2, 7))
println(add3(1, 2, 7))
}
```

Expand All @@ -155,7 +155,7 @@ The expression `add3(1, 2, 7)` returns `10`. Any expression that evaluates to a
fn init {
let f = fn (x) { x + 1 }
let g = fn (x) { x + 2 }
print((if true { f } else { g })(3)) // OK
println((if true { f } else { g })(3)) // OK
}
```

Expand Down Expand Up @@ -252,8 +252,8 @@ and `ArgsLoc`, which is a array containing the source location of each argument,

```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)")
println("loc of whole function call: \{loc}")
println("loc of arguments: \{args_loc}")
}
fn init {
Expand Down Expand Up @@ -423,7 +423,7 @@ fn main {
let sum =
for i = 1, acc = 0; i <= 6; i = i + 1 {
if i % 2 == 0 {
println("even: \(i)")
println("even: \{i}")
continue i + 1, acc + i
}
} else {
Expand Down Expand Up @@ -567,7 +567,7 @@ MoonBit supports string interpolation. It enables you to substitute variables wi
```moonbit live
fn init {
let x = 42
print("The answer is \(x)")
println("The answer is \{x}")
}
```

Expand Down Expand Up @@ -609,7 +609,7 @@ fn pack(a: Bool, b: Int, c: String, d: Double) -> (Bool, Int, String, Double) {
fn init {
let quad = pack(false, 100, "text", 3.14)
let (bool_val, int_val, str, float_val) = quad
println("\(bool_val) \(int_val) \(str) \(float_val)")
println("\{bool_val} \{int_val} \{str} \{float_val}")
}
```

Expand All @@ -622,9 +622,9 @@ fn f(t : (Int, Int)) -> Unit {
let x2 = t.0
let y2 = t.1
if (x1 == x2 && y1 == y2) {
print("yes")
println("yes")
} else {
print("no")
println("no")
}
}
Expand All @@ -649,7 +649,7 @@ fn init {
let a = numbers[2]
numbers[3] = 5
let b = a + numbers[3]
print(b) // prints 8
println(b) // prints 8
}
```

Expand All @@ -662,7 +662,7 @@ MoonBit provides a hash map data structure that preserves insertion orde called
let map : Map[String, Int] = { "x": 1, "y": 2, "z": 3 }
```

Currently keys in map literal syntax must be constant. `Map`s can also be destructed elegantly with pattern matching, see [Map Pattern[#map-pattern].
Currently keys in map literal syntax must be constant. `Map`s can also be destructed elegantly with pattern matching, see [Map Pattern](#map-pattern).

## Json literal

Expand All @@ -688,7 +688,7 @@ let zero = 0
fn init {
let mut i = 10
i = 20
print(i + zero)
println(i + zero)
}
```

Expand Down Expand Up @@ -827,13 +827,13 @@ fn print_list(l: List) -> Unit {
// in additional to deciding which case a value belongs to
// you can extract the payload data inside that case
match l {
Nil => print("nil")
Nil => println("nil")
// Here `x` and `xs` are defining new variables instead of referring to existing variables,
// if `l` is a `Cons`, then the payload of `Cons` (the first element and the rest of the list)
// will be bind to `x` and `xs
Cons(x, xs) => {
print(x)
print(",")
println(x)
println(",")
print_list(xs)
}
}
Expand Down Expand Up @@ -1211,7 +1211,7 @@ 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.

```moonbit
let result =
let result =
MyStringBuilder::new()
..add_char('a')
..add_char('a')
Expand Down Expand Up @@ -1247,57 +1247,58 @@ fn div(x: Int, y: Int) -> Int!String {

The keyword `raise` is used to interrupt the function execution and return an error. There are three ways to handle errors in functions:

- Using the `!` suffix to rethrow the error directly in case of an error, for example:
- Append `!` before function name to rethrow the error directly in case of an error, for example:

```moonbit
fn div_reraise(x: Int, y: Int) -> Int!String {
div(x, y)! // Rethrow the error if `div` raised an error
div!(x, y) // Rethrow the error if `div` raised an error
}
```

- Using `try` and `catch` to catch and handle errors, for example:

```moonbit
fn div_with_default(x: Int, y: Int, default: Int) -> Int {
fn div_with_default(x : Int, y : Int, default : Int) -> Int {
try {
div(x, y)!
div!(x, y)
} catch {
s => { println(s); default }
s => {
println(s)
default
}
}
}
```

Here, `try` is used to call a function that might throw an error, and `catch` is used to match and handle the caught error. If no error is caught, the catch block will not be executed.

- Using the `!!` suffix to convert the result into a first-class value of the `Result` type, for example:
- Append `?` before function name to convert the result into a first-class value of the `Result` type, for example:

```moonbit
test {
let res = div(6, 3)!!
inspect(res, content="Ok(2)")!
let res = div(6, 0)!!
inspect(res, content="Err(division by zero)")!
let res = div?(6, 3)
inspect!(res, content="Ok(2)")
let res = div?(6, 0)
inspect!(res, content="Err(division by zero)")
}
```

The suffix `!!` is a syntax sugar that is equivalent to the following code:
`?` is a syntactic sugar that is equivalent to the following code:

```moonbit
test {
let res = try { Ok(div(6, 3)!) } catch { s => Err(s) }
let res = try { Ok(div!(6, 3)) } catch { s => Err(s) }
}
```

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:
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 `?` can only be used at the function call site and not in other expressions. Valid ones include:

```moonbit
f(x)!
x.f()!
(x |> f)!
(x + y)!
f!(x)
x.f!()
```

Additionally, if the return type of a function includes an error type, the function call must use `!` or `!!` for error handling, otherwise the compiler will report an error.
Additionally, if the return type of a function includes an error type, the function call must use `!` or `?` for error handling, otherwise the compiler will report an error.

## Generics

Expand Down Expand Up @@ -1326,7 +1327,7 @@ fn reduce[S, T](self: List[S], op: (T, S) -> T, init: T) -> T {

## Access Control

By default, all function definitions and variable bindings are *invisible* to other packages; types without modifiers are abstract data types, whose name is exported but the internals are invisible. This design prevents unintended exposure of implementation details. You can use the `pub` modifier before `type`/`enum`/`struct`/`let` or top-level function to make them fully visible, or put `priv` before `type`/`enum`/`struct` to make it fully invisible to other packages. You can also use `pub` or `priv` before field names to obtain finer-grained access control. However, it is important to note that:
By default, all function definitions and variable bindings are _invisible_ to other packages; types without modifiers are abstract data types, whose name is exported but the internals are invisible. This design prevents unintended exposure of implementation details. You can use the `pub` modifier before `type`/`enum`/`struct`/`let` or top-level function to make them fully visible, or put `priv` before `type`/`enum`/`struct` to make it fully invisible to other packages. You can also use `pub` or `priv` before field names to obtain finer-grained access control. However, it is important to note that:

- 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.
Expand Down Expand Up @@ -1382,10 +1383,10 @@ fn init {
}
// Package B
fn print(r : RO) -> Unit {
print("{ field: ")
print(r.field) // OK
print(" }")
fn println(r : RO) -> Unit {
println("{ field: ")
println(r.field) // OK
println(" }")
}
fn init {
let r : RO = { field: 4 } // ERROR: Cannot create values of the public read-only type RO!
Expand Down Expand Up @@ -1497,9 +1498,9 @@ fn init {
fn print_array_view[T : Show](view : ArrayView[T]) -> Unit {
for i=0; i<view.length(); i = i + 1 {
print(view[i])
println(view[i])
}
print("\n")
println("\n")
}
```

Expand All @@ -1521,7 +1522,7 @@ pub fn length[A](self : MyList[A]) -> Int {
}
pub fn op_as_view[A](self : MyList[A], ~start : Int, ~end : Int) -> MyListView[A] {
println("op_as_view: [\(start),\(end))")
println("op_as_view: [\{start},\{end})")
if start < 0 || end > self.length() { abort("index out of bounds") }
{ ls: self, start, end }
}
Expand Down Expand Up @@ -1650,7 +1651,7 @@ trait Default {

## Access control of methods and direct implementation of traits

To make the trait system coherent (i.e. there is a globally unique implementation for every `Type: Trait` pair), and prevent third-party packages from modifying behavior of existing programs by accident, *only the package that defines a type can define methods for it*. So one cannot define new methods or override old methods for builtin and foreign types.
To make the trait system coherent (i.e. there is a globally unique implementation for every `Type: Trait` pair), and prevent third-party packages from modifying behavior of existing programs by accident, _only the package that defines a type can define methods for it_. So one cannot define new methods or override old methods for builtin and foreign types.

However, it is often useful to implement new traits for an existing type. So MoonBit provides a mechanism to directly implement a trait, defined using the syntax `impl Trait for Type with method_name(...) { ... }`. Type annotations can be omitted from `impl`, because MoonBit can infer the correct types from the trait's signature. For example, to implement a new trait `ToMyBinaryProtocol` for builtin types, one can (and must) use `impl`:

Expand All @@ -1665,7 +1666,7 @@ impl ToMyBinaryProtocol for Int with to_my_binary_protocol(x, b) { ... }
impl[X : ToMyBinaryProtocol] for Array[X] with to_my_binary_protocol(arr, b) { ... }
```

When searching for the implementation of a trait, `impl`s have a higher priority, so they can be used to override ordinary methods with undesirable behavior. `impl`s can only be used to implement the specified trait. They cannot be called directly like ordinary methods. Furthermore, *only the package of the type or the package of the trait can define an implementation*. For example, only `@pkg1` and `@pkg2` are allowed to define `impl @pkg1.Trait for @pkg2.Type` for type `@pkg2.Type`. This restriction ensures that MoonBit's trait system is still coherent with the extra flexibility of `impl`s.
When searching for the implementation of a trait, `impl`s have a higher priority, so they can be used to override ordinary methods with undesirable behavior. `impl`s can only be used to implement the specified trait. They cannot be called directly like ordinary methods. Furthermore, _only the package of the type or the package of the trait can define an implementation_. For example, only `@pkg1` and `@pkg2` are allowed to define `impl @pkg1.Trait for @pkg2.Type` for type `@pkg2.Type`. This restriction ensures that MoonBit's trait system is still coherent with the extra flexibility of `impl`s.

To invoke an trait implementation directly, one can use the `Trait::method` syntax:

Expand All @@ -1675,7 +1676,7 @@ trait MyTrait {
}
fn MyTrait::f(self: Int) -> Unit {
println("Got Int \(self)!")
println("Got Int \{self}!")
}
fn init {
Expand Down Expand Up @@ -1781,8 +1782,8 @@ MoonBit provides the test code block for writing test cases. For example:

```moonbit
test "test_name" {
@test.eq(1 + 1, 2)!
@test.eq(2 + 2, 4)!
@test.eq!(1 + 1, 2)
@test.eq!(2 + 2, 4)
}
```

Expand All @@ -1798,7 +1799,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.

```moonbit
````moonbit
/// Return a new array with reversed elements.
///
/// # Example
Expand All @@ -1809,11 +1810,11 @@ Doc comments are comments prefix with `///` in each line in the leading of tople
fn reverse[T](xs : Array[T]) -> Array[T] {
...
}
```
````

### Pragmas

Pragmas are annotations inside doc comments. They all take the form `/// @word ...`. The *word* indicates the type of pragma and is followed optionally by several *word* or string literals. Pragmas do not normally affect the meaning of programs. Unrecognized pragmas will be reported as warnings.
Pragmas are annotations inside doc comments. They all take the form `/// @word ...`. The _word_ indicates the type of pragma and is followed optionally by several _word_ or string literals. Pragmas do not normally affect the meaning of programs. Unrecognized pragmas will be reported as warnings.

- Alert Pragmas

Expand Down
6 changes: 3 additions & 3 deletions tour.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ struct User {
fn greetUser(self: User) -> String{ // a method of struct/type/class `User`
let id = self.id
let name = self.name
"Greetings, \(name) of id \(id)" // string interpolation
"Greetings, \{name} of id \{id}" // string interpolation
}
// construct a User object.
let evan: User = {id:0,name:"Evan",email:"[email protected]"}
Expand Down Expand Up @@ -241,7 +241,7 @@ fn to_string[T: Printable](self : List[T]) -> String {
fn to_string_aux[T: Printable](self: List[T]) -> String{
match self {
Nil => ""
Cons(x,xs) => "\(x) " + to_string_aux(xs)
Cons(x,xs) => "\{x} " + to_string_aux(xs)
}
}
```
Expand Down Expand Up @@ -306,7 +306,7 @@ fn greetUserAlt(self: User) -> String {
let { id: id, name: name, email: _ } = self
// equivalent, but ignores the rest.
let {id,name,..} = self
"Greetings, \(name) of id \(id)"
"Greetings, \{name} of id \{id}"
}
```

Expand Down
Loading

0 comments on commit 1254ed5

Please sign in to comment.