Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Chapter 5 #23

Merged
merged 9 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/00-course-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The course is suitable for a wide audience, from programming beginners to experi
| 2 | [Development Environments & Expressions](./development-environments-expressions) | Pending | Pending | |
| 3 | [Functions, Lists & Recursion](./functions-lists-recursion) | Pending | Pending | |
| 4 | [Tuples, Structs & Enums](./tuples-structs-enums) | Pending | Pending | |
| 5 | Trees | Pending | Pending | |
| 5 | [Trees](./trees) | Pending | Pending | |
| 6 | Generics & Higher-Order Functions | Pending | Pending | |
| 7 | Imperative Programming | Pending | Pending | |
| 8 | Queues | Pending | Pending | |
Expand Down
2 changes: 2 additions & 0 deletions docs/02-development-environments-expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ In MoonBit, the type for Boolean values is `Bool`, and it can only have two poss

In MoonBit, `==` represents a comparison between values. In the above examples, the left-hand side is an expression, and the right-hand side is the expected result. In other words, these examples themselves are expressions of type `Bool`, and we expect their values ​​to be `true`.

The `||` and `&&` operators are short-circuited. This means that if the outcome of the entire expression can be determined, the calculation will be halted, and the result will be immediately returned. For instance, in the case of `true || ...`, it is evident that `true || any value` will always yield true. Therefore, only the left side of the `||` operator needs to be evaluated. Similarly, when evaluating `false && ...`, since it is known that `false && any value` will always be false, the right side is not evaluated either. In this case, if the right side of the operator contains side effects, those side effects may not occur.

Quiz: How to define XOR (true if only one is true) using OR, AND, and NOT?

#### Integers
Expand Down
38 changes: 38 additions & 0 deletions docs/03-functions-lists-recursion.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,44 @@ For example:

Since `->` is right-associative, in the last example, the brackets in the return type can be omitted.

### Labeled Arguments and Optional Arguments

It is not uncommon to encounter difficulties recalling the order of parameters when calling a function, particularly when multiple parameters share the same type. In such situations, referring to documentation or IDE prompts can be helpful. However, when reviewing code written by others, these resources may not be readily available. To overcome this challenge, labeled arguments offer a practical solution. In MoonBit, we can make a parameter "labeled" by prefixing it with `~`. For instance, consider the following code snippet:

```moonbit
fn greeting1(~name: String, ~location: String) -> Unit {
println("Hi, \(name) from \(location)!")
}

fn init {
greeting1(~name="somebody", ~location="some city")
let name = "someone else"
let location = "another city"
// `~label=label` can be abbreviated as `~label`
greeting1(~name, ~location)
}
```

By using labeled arguments, the order of the parameters becomes less important. In addition, they can be made optional by specifying a default value when declaring them. When the function is called, if no argument is explicitly provided, the default value will be used.

Consider the following example:

```moonbit
fn greeting2(~name: String, ~location: Option[String] = None) -> Unit {
match location {
Some(location) => println("Hi, \(name)!")
None => println("Hi, \(name) from \(location)!")
}
}

fn init {
greeting2(~name="A") // Hi, A!
greeting2(~name="B", ~location=Some("X")) // Hi, B from X!
}
```

It is important to note that the default value expression will be evaluated each time the function is called.

## Lists

Data is everywhere. Sometimes, we have data with the following characteristics:
Expand Down
31 changes: 30 additions & 1 deletion docs/04-tuples-structs-enums.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ Additionally, enumerated types prevent the representation of irrational data. Fo

Each variant of an enumerated type can also carry data. For instance, we've seen the enumerated type `Option`.

```moonbit
```moonbit no-check
enum Option[T] {
Some(T)
None
Expand All @@ -251,6 +251,35 @@ enum ComputeResult {

To do this, simply enclose parameters with parentheses and separate them by commas after each variant. In the second example, we define the case of successful integer operation, and the value is an integer. Enumerated types correspond to a distinguishable union. What does that mean? First, it is a union of different cases, for example, the set represented by the type `T` for `Some` and the set defined by the singular value `None`. Second, this union is distinguishable because each case has a unique name. Even if there are two cases with the same data type, they are entirely different. Thus, enumerated types are also known as sum types.

### Labeled Arguments

Similar to functions, enum constructors also support the use of labeled arguments. This feature is beneficial in simplifying pattern matching patterns. For example:

```moonbit
enum Tree[X] {
Nil
Branch(X, ~left : Tree[X], ~right : Tree[X])
}

fn leftmost[X](self : Tree[X]) -> Option[X] {
loop self {
Nil => None
// use `label=pattern` to match labeled arguments of constructor
Branch(top, left=Nil, right=Nil) => Some(top)
// `label=label` can be abbreviated as `~label`
Branch(_, left=Nil, ~right) => continue right
// use `..` to ignore all remaining labeled arguments
Branch(_, ~left, ..) => continue left
}
}

fn init {
// syntax for creating constructor with labeled arguments is the same as calling labeled function
let t: Tree[Int] = Branch(0, right=Nil, left=Branch(1, left=Nil, right=Nil))
println(t.leftmost()) // `Some(1)`
}
```

## Algebraic Data Types

We've mentioned product types and sum types. Now, let me briefly introduce algebraic data types. It's important to note that this introduction to algebraic data types is quite basic. Please read the references for a deeper understanding.
Expand Down
Loading