Skip to content

Commit fd3809e

Browse files
committed
add course9/course_en.md
1 parent 29025df commit fd3809e

File tree

2 files changed

+308
-0
lines changed

2 files changed

+308
-0
lines changed

course9/course_en.md

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
---
2+
marp: true
3+
math: mathjax
4+
paginate: true
5+
backgroundImage: url('../pics/background_moonbit.png')
6+
---
7+
8+
<!--
9+
```moonbit
10+
enum Tree[T] {
11+
Empty
12+
Node(T, Tree[T], Tree[T])
13+
}
14+
15+
struct Queue[T] {
16+
mut array: Array[T]
17+
mut start: Int
18+
mut end: Int
19+
mut length: Int
20+
}
21+
```
22+
-->
23+
24+
# Modern Programming Ideology
25+
26+
## Traits
27+
28+
### MoonBit Open Course Team
29+
30+
---
31+
32+
# Recapitulation
33+
34+
- Balanced Binary Search Trees (Chapter 6)
35+
- Define a more general balanced BST, capable of storing various data types.
36+
```moonbit no-check
37+
enum Tree[T] {
38+
Empty
39+
Node(T, Tree[T], Tree[T])
40+
}
41+
42+
// We need a comparison function to determine the order of values
43+
// -1: less than; 0: equal to; 1: greater than
44+
fn insert[T](self: Tree[T], value: T, compare: (T, T) -> Int) -> Tree[T]
45+
fn delete[T](self: Tree[T], value: T, compare: (T, T) -> Int) -> Tree[T]
46+
```
47+
- Circular Queues (Chapter 8)
48+
- Initialize the array with the default value of the type.
49+
```moonbit no-check
50+
fn make[T]() -> Queue[T] {
51+
{ array: Array::make(5, T::default()), start: 0, end: 0, length: 0 }
52+
}
53+
```
54+
55+
---
56+
57+
# Methods
58+
59+
Some functions may associate with a type `T`.
60+
- Compare two `T` values: `fn T::compare(self: T, other: T) -> Int`
61+
- Get the default value of `T`: `fn T::default() -> T`
62+
- Get the string representation of a `T` value: `fn T::to_string(self: T) -> String`
63+
- ...
64+
65+
Such functions are called the **methods** of `T`.
66+
67+
---
68+
69+
# Traits
70+
71+
Traits declare a list of operations to be supplied if a type wants to implement it.
72+
73+
```moonbit
74+
trait Compare {
75+
compare(Self, Self) -> Int
76+
// `Self` refers to the type that implements the trait.
77+
}
78+
trait Default {
79+
default() -> Self
80+
}
81+
```
82+
83+
- The trait system in MoonBit is structural.
84+
- There is no need to implement a trait explicitly.
85+
- Types with the required methods automatically implements a trait.
86+
87+
---
88+
89+
# Traits
90+
91+
- In generic functions, we use traits as bounds to specify what methods a type supports.
92+
- `<type>: <trait>` requires `<type>` to be bound by `<trait>`;
93+
- The methods of a trait can then be called via `<type>::<method>()`.
94+
95+
```moonbit no-check
96+
fn make[T: Default]() -> Queue[T] { // `T` should support the `default` method.
97+
{
98+
array: Array::make(5, T::default()), // The return type of `default` is `T`.
99+
start: 0, end: 0, length: 0
100+
}
101+
}
102+
```
103+
104+
- With traits, we can timely detect errors caused by calling missing methods.
105+
![height:150px](../pics/no_method.png)
106+
107+
---
108+
109+
# Traits
110+
111+
```moonbit
112+
fn insert[T : Compare](tree : Tree[T], value : T) -> Tree[T] {
113+
// Since `T` is bound by `Compare`, it should support the `compare` method.
114+
match tree {
115+
Empty => Node(value, Empty, Empty)
116+
Node(v, left, right) =>
117+
if T::compare(value, v) == 0 { // We can call `compare` here.
118+
tree
119+
} else if T::compare(value, v) < 0 { // We can call `compare` here.
120+
Node(v, insert(left, value), right)
121+
} else {
122+
Node(v, left, insert(right, value))
123+
}
124+
}
125+
}
126+
```
127+
128+
---
129+
130+
# Definition of Methods
131+
132+
- Methods can be defined using the syntax `fn <type>::<method>(...) -> ...`.
133+
134+
```moonbit
135+
struct BoxedInt { value : Int }
136+
137+
fn BoxedInt::default() -> BoxedInt {
138+
// By defining the `default` method, the `Default` trait is now implemented.
139+
{ value : Int::default() }
140+
// The default value can be defined by boxing the default value of `Int`.
141+
}
142+
```
143+
```moonbit no-check
144+
fn init {
145+
let array: Queue[BoxedInt] = make()
146+
}
147+
```
148+
149+
---
150+
151+
# Method Chaining
152+
153+
- In addition to `<type>::<method>(<expr>, ...)`, we can as well call the method using `<expr>.<method>(...)`, given `<expr>` is of type `<type>`.
154+
155+
```moonbit
156+
fn BoxedInt::plus_one(b: BoxedInt) -> BoxedInt {
157+
{ value : b.value + 1 }
158+
}
159+
fn plus_two(self: BoxedInt) -> BoxedInt {
160+
// `<type>::` can be omitted when the parameter name is `self`.
161+
{ value : self.value + 2}
162+
}
163+
164+
fn init {
165+
let _five = { value: 1 }.plus_one().plus_one().plus_two()
166+
// This avoids multiple nesting of method calls.
167+
let _five = plus_two(plus_one(plus_one({value: 1})))
168+
}
169+
```
170+
171+
---
172+
173+
# Automatically Derive Builtin Traits
174+
175+
- Some simple builtin traits can be automatically derived by adding `derive(<traits>)` after the type definition.
176+
177+
```moonbit no-check
178+
struct BoxedInt { value : Int } derive(Default, Eq, Compare, Debug)
179+
```
180+
181+
- The member data types should have implemented the same traits.
182+
183+
---
184+
185+
# Using Traits to Implement a Map
186+
187+
- A map is a collection of key-value pairs.
188+
- Each **key** corresponds to a **value**.
189+
- Example: `{ 0 -> "a", 5 -> "Hello", 7 -> "a"}`.
190+
191+
```moonbit no-check
192+
type Map[Key, Value]
193+
194+
// Create a map
195+
fn make[Key, Value]() -> Map[Key, Value]
196+
// Add a key-value pair, or update the corresponding value of a key
197+
fn put[Key, Value](map: Map[Key, Value], key: Key, value: Value) -> Map[Key, Value]
198+
// Get the corresponding value of a key
199+
fn get[Key, Value](map: Map[Key, Value], key: Key) -> Option[Value]
200+
```
201+
202+
---
203+
204+
# Using Traits to Implement a Map
205+
206+
- A simple implementation:
207+
- Store key-value pairs using a list of pairs.
208+
- Add/update a key-value pair by inserting the pair to the beginning of the list.
209+
- Search the list from the beginning until the first matching key is found.
210+
- We need to compare the key we are looking for with the keys stored in the list.
211+
- The `Key` type should implement the `Eq` trait.
212+
```moonbit no-check
213+
fn get[Key: Eq, Value](map: Map[Key, Value], key: Key) -> Option[Value]
214+
```
215+
216+
---
217+
218+
# Using Traits to Implement a Map
219+
220+
- Store key-value pairs using a list of pairs.
221+
```moonbit
222+
// Define `Map[Key, Value]` to be `List[(Key, Value)]`
223+
type Map[Key, Value] List[(Key, Value)]
224+
225+
fn make[Key, Value]() -> Map[Key, Value] {
226+
Map(Nil)
227+
}
228+
229+
fn put[Key, Value](map: Map[Key, Value], key: Key, value: Value) -> Map[Key, Value] {
230+
let Map(original_map) = map
231+
Map( Cons( (key, value), original_map ) )
232+
}
233+
```
234+
235+
---
236+
237+
# Using Traits to Implement a Map
238+
239+
- Store key-value pairs using a list of pairs.
240+
```moonbit
241+
fn get[Key: Eq, Value](map : Map[Key, Value], key : Key) -> Option[Value] {
242+
fn aux(list : List[(Key, Value)]) -> Option[Value] {
243+
match list {
244+
Nil => None
245+
Cons((k, v), tl) => if k == key {
246+
// `Key` is bound by `Eq`, so we can call `==` directly.
247+
Some(v)
248+
} else {
249+
aux(tl)
250+
}
251+
}
252+
}
253+
254+
aux(map.0) // Use `.0` to get the value.
255+
}
256+
```
257+
258+
---
259+
260+
# Custom Operators
261+
262+
- Operators can be customized by defining methods with specific names and types.
263+
264+
```moonbit
265+
fn BoxedInt::op_equal(i: BoxedInt, j: BoxedInt) -> Bool {
266+
i.value == j.value
267+
}
268+
fn BoxedInt::op_add(i: BoxedInt, j: BoxedInt) -> BoxedInt {
269+
{ value: i.value + j.value }
270+
}
271+
272+
fn init {
273+
let _ = { value: 10 } == { value: 100 } // false
274+
let _ = { value: 10 } + { value: 100 } // { value: 110 }
275+
}
276+
```
277+
278+
---
279+
280+
# Custom Operators
281+
282+
- Operators can be customized by defining methods with specific names and types.
283+
284+
```moonbit
285+
// map [ key ]
286+
fn Map::op_get[Key: Eq, Value](map: Map[Key, Value], key: Key) -> Option[Value] {
287+
get(map, key)
288+
}
289+
// map [ key ] = value
290+
fn Map::op_set[Key: Eq, Value](map: Map[Key, Value], key: Key, value: Value) -> Map[Key, Value] {
291+
put(map, key, value)
292+
}
293+
294+
fn init {
295+
let empty: Map[Int, Int] = make()
296+
let one = { empty[1] = 1 } // let one = Map::op_set(empty, 1, 1)
297+
let _ = one[1] // let _ = Map::op_get(one, 1)
298+
}
299+
```
300+
301+
---
302+
303+
# Summary
304+
305+
- In this chapter, we learned how to
306+
- Define traits and use them to bound type parameters
307+
- Implement methods and custom operators
308+
- Implement a simple map using traits in MoonBit

course9/lecture_en.md

Whitespace-only changes.

0 commit comments

Comments
 (0)