MoonBitのチュートリアルをやってみた

# 概要

(2025年6月1日現在, v0.120250416の) MoonBitのチュートリアルをやってみた記録です。

https://docs.moonbitlang.com/en/latest/tutorial/index.html

# MoonBitとは

> MoonBit is an end-to-end programming language toolchain for cloud and edge computing using WebAssembly.

「WebAssemblyを基盤としたプログラミング言語とツールチェイン」と思っておけば良さそう。

開発元は IDEA(International Digital Economy Academy, 粤港澳大湾区数字经济研究院)という中国の研究機関のようです。

# language tour

まずはこのlanguage tourをやってみます。

https://tour.moonbitlang.com/

なお、2025年6月1日現在、Qiitaにmoonbitのシンタクスハイライトがないため、以下rustのシンタクスハイライトで代用しています。

## hello

まずは、helloです。

```rust
fn main {
  println("hello")
}
```

- エントリーポイントはmain
- printlnで改行つきprint
- fnで関数定義
- {}でブロックを作成
- ;は必要ない

といった感じでしょうか。Cライク、というかRustライクな感じですね。

## 変数

続いて変数。

```rust
fn main {
  let a : Int = 10 
  let b = 20 
  println(a + b)

  let mut  c = 10 
  c = c + 1
  println(c)

  let d = 20 
  // d = d + 1
  println(d)
}
```

- letで変数定義
- :のあとに型指定
- 整数型はInt
- 型指定は省略可能で値から型推論される
- Int型同士で+演算が可能
- 変数はデフォルトでイミュータブル
- mutをつけるとミュータブルになる
- コメントは//

## 数値

数値です。

```rust
fn main {
  let dec : Int = 1000000
  let dec2 : Int = 1_000_000 
  let hex : Int = 0xFFFF 
  let oct = 0o777
  let bin = 0b1001 

  println(1 + 2)
  println(1 - 2)
  println(1 * 2)
  println(5 / 2)
  println(10 % 3)

  let num1 : Double = 3.14
  let num2 : Float = 3.14
}
```

- 整数型Intは32ビット符号付き整数
- _を数値リテラルに挿入できる (そのほうが読みやすければ)
- プレフィックス0xで16進数、0oで8進数、0bで2進数の記述が可能
- +,*,-,/,%などの演算子を使用可能
- 浮動小数点型にはDouble型とFloat型がある

## 関数

```rust
fn add(a : Int, b : Int) -> Int {
  return a + b
}

fn compute() -> Unit {
  println(add(2, 40))
}

fn main {
  compute()
}
```

- fnで関数定義
- ()内に引数を記述、->で戻り値の型を記述
- Unit型は戻り値がない場合 (Cのvoid相当)
- returnで戻り値を返却
- ()内に実引数を並べて呼び出し

## ブロック

```rust
fn main {
  let a = 100

  {
    let mut a = 0
    println("checkpoint 1")
    a = a + 1
  }

  println("checkpoint 2")
  println(f())
}

fn f() -> Int {
  let b = 3.14

  let result = {
    let b = 1
    println("checkpoint 3")
    b + 5 
  }

  result // same as `return result` here
}
```

- 即時関数呼び出しのようなもの
- いくつか文を実行し、最後の式の値を返す (返さなくても良い)
- 名前空間的には別のスコープを形成して、外側の変数をshadowingする
- 関数の最後の式はreturnをつけなくても、戻り値として評価される

## 配列

```rust
fn main {
  let arr1 : Array[Int] = [1, 2, 3, 4, 5]
  let arr2 = Array::make(4,1) 

  println(arr1.length()) // get the length of the array
  println(arr1[1]) // get the second element of the array

  // We can also use the spread operator to concatenate arrays.
  let arr3 = [..arr1, 1000, 2000, ..arr2, 3000, 4000]
  println("spread arrays:")
  println(arr3)

  let view : ArrayView[Int] = arr1[1:4]
  println("array view:")
  println(view)
  println("view[0]:")
  println(view[0])

  arr1.push(6) // push an element to the end
  println("updated array:")
  println(arr1)
}
```

- Arrayは可変長配列
- カンマ区切り[]でリテラルをかけるし、Array::make()で初期化しても良い
- Array::make()はサイズと初期化する値(全部がそれで埋められる)の2つの引数を取る
- length()メソッドあり
- インデキシングは[]を使用
- 既存の配列を展開するスプレッド構文あり
- [start:end]でビューを作成できる(コピーコスト削減)
- push()メソッドで要素を追加できる
- mutをつけないデフォルトのイミュータブルは、再代入禁止を意味していて、Arrayの場合要素はミュータブルになる

## 文字列

```rust
fn main {
  let str = "Hello, World!"
  // Access a character by index.
  let c : Char = str[4]
  println(c)
  let c2 = 'o'
  println(c == c2)

  // Use escape sequence.
  println("\nHello, \tWorld!")
  println("unicode \u{1F407} is a rabbit")

  // Concatenate two strings.
  println(str + " Hello, MoonBit!")

  // Use string interpolation.
  let moon = "Moon"
  let bit = "Bit"
  println("Use \{moon + bit}. Happy coding")
}
```

- 文字列 String はUTF-16エンコードされた文字(Char)のシーケンス
- 文字列リテラルはダブルクォーテーションで囲む
- 文字はシングルクォーテーションで囲む
- 文字列に対して[]演算子を使用してインデックスアクセスが可能
- エスケープ文字としては改行\n, タブ\t, バックスラッシュ"\\\\, ダブルクォーテーション\\", シングルクォーテーション\\'など
- \\u{1F407}など、ユニコードのコードポイントを記述するエスケープも使用可能
- +で文字列連結
- リテラル内は\\{式}で文字列補間(string interpolation)ができる

## タプル

```rust
fn main {
  // create Tuple 
  let tuple = (3.14, false, [1,2,3])  
  let tuple2 : (Float, Bool, Int) = (2.1, true, 20)
  println(tuple)

  // Accessing tuple elements
  println(tuple.0)
  println(tuple.2)

  // Tuple can also be destructured. 
  let (a, b, c) = f()
  println("\{a}, \{b}, \{c}")
}

fn f() -> (Int, Bool, Double) {
  (1, false, 3.14) // return multiple values via tuple
}
```

- タプルは異なる変数の組
- イミュータブル
- ()内,区切りで生成
- アクセスはドットアクセス(.0, .1など)で[]アクセスではない
- let (a, b) = tup で分解して変数に束縛することができる
- 関数から複数の値を返したいとき、タプルを使用すれば良い

## マップ

```rust
fn main {
  // Create a map by map literal
  let map1 = { "key1": 1, "key2": 2, "key3": 3 }
  println(map1)
  // You can also create a map by Map::of, from a list of key-value pairs
  let map2 = Map::of([("key1", 1), ("key2", 2), ("key3", 3)])
  println(map1 == map2)

  // Access a value by key
  println(map1.get("key1"))

  // Update a value by key
  map1["key1"] = 10
  println(map1)

  // test a if a key exists
  println(map1.contains("key1"))
}
```

- マップはいわゆる連想配列でキーバリューペアのコレクション
- ミュータブル
- キーもバリューも基本型(Int, String, Bool, Double, ...)であれば、マップリテラルで生成する
- そうでない場合はMap::of()で生成可能・これは引数にキーバリューペア(タプル)の配列を取る
- マップの値にアクセスするには[]を使用するか、get()メソッドを使用する
- マップの値を更新するにはm[key] = valueとすればよい
- マップにキーが含まれているかを判定するには、contains()メソッドを使用する

## if

```rust
fn fib(x : Int) -> Int {
  if x < 2 {
    x
  } else {
    fib(x - 1) + fib(x - 2)
  }
}

fn main {
  if 5 > 1 {
    println("5 is greater than 1")
  }
  println(fib(5))
  println(weekday(3))
}

fn weekday(x : Int) -> String {
  if x == 1 {
    "Monday"
  } else if x == 2 {
    "Tuesday"
  } else if x == 3 {
    "Wednesday"
  } else if x == 4 {
    "Thursday"
  } else if x == 5 {
    "Friday"
  } else if x == 6 {
    "Saturday"
  } else if x == 7 {
    "Sunday"
  } else {
    "Invalid day"
  }
}
```

- if, else if ..., elseで条件分岐を記述
- 条件部分に()は不要
- ifは式であり、if, elseで各ブロックの最後の式を評価して返す
- else節がなければUnit型とみなされる

## ループ

```rust
fn main {
  let array = [1, 2, 3]

  println("for loop:")
  for i = 0; i < array.length(); i = i + 1 {
    println("array[\{i}]: \{array[i]}")
  }

  println("\nfunctional for loop:")

  let sum = for i = 1, acc = 0; i <= 10; i = i + 1 {
    let even = i % 2 == 0
    continue i + 1, acc + i
  } else {
    acc
  }
  println(sum)

  println("\nwhile loop:")
  let mut j = 0
  while true {
    println("array[\{j}]: \{array[j]}")
    j = j + 1
    if j < array.length() {
      continue
    } else {
      break
    }
  }
}
```

- for式は、Cスタイルのinit; condition; incrementで書く
- while式も、Cスタイルのwhile conditionで書く
- ループも式であり、最後にelse節で式を評価するか、または途中でループを脱出する場合はbreakのあとに続けて書かれた式を評価する
- for文の場合、continueのあとに書かれた式で、初期化で定義された変数を更新する

## for-inループ

```rust
fn main {
  println("for-in loop:")
  let array = [1, 2, 3]
  for element in array {
    println("element: \{element}")
  }

  println("for-in loop with index:")
  for i, element in array {
    println("index: \{i}, element: \{element}")
  }
  
  println("for-in loop for map:")
  let map = { "key1": 1, "key2": 2, "key3": 3 }
  for k, v in map {
    println("key: \{k}, value: \{v}")
  }
}
```

- 配列やマップなどのコレクションを繰り返す場合は、for-inループを使用できる
- for-inループの対象になるコレクションとは、イテレータIter[V]を返すiter()や、イテレータIter2[V]を返すiter2()メソッドを実装している型の変数

## range

```rust
fn main {
  println("number 1 to 3:")
  for i in 1..<4 {
    println(i)
  }
  
  println("number 1 to 4:")
  for i in 1..=4 {
    println(i)
  }
}
```

- rangeもfor-inループの対象にできる
- 右端open, closeは..<と..=で区別できる (Perl6のneko operatorを彷彿とさせるが、これは右端のみ)

## エラー処理

```rust
///|
pub fn safe_add(i : Int, j : Int) -> Int! {
  let signum_i = i & 0x80000000
  let signum_j = j & 0x80000000
  let result = i + j
  if signum_i != signum_j {
    result
  } else {
    let result_signum = result & 0x80000000
    if result_signum != signum_i {
      fail!("overflow")
    } else {
      result
    }
  }
}

///|
fn main {
  let a = try {
    safe_add!(1, 2)
  } catch {
    _ => panic()
  }
  try {
    let result = safe_add!(@int.max_value, @int.max_value)

  } catch {
    Failure(error_message) => println(error_message)
    _ => panic()
  }
  let result = safe_add?(@int.max_value, @int.max_value)

}
```

- 戻り価の型に!をつけると、エラーしうる関数という表現になる
- fail!()でエラーを発生させられる
- エラーはtry-catchで補足するか?を使用してResult型に変換する
- catch節ではパターンマッチを使える
- panic()はプログラムを終了させる

## テスト

```rust
test {
  assert_eq!(1, 1 + 2)
  assert_false!(1 == 2)
  assert_true!([1,2,3] == [1,2,3])
}

test {
  inspect!(fib(5))
  inspect!([1,2,3,4].map(fib))
}

// Add test name to make it more descriptive.
test "fibonacci" {
  inspect!(fib(5), content="5")
  inspect!(fib(6), content="8")
}

fn fib(n : Int) -> Int {
  if n < 2 {
    n
  } else {
    fib(n - 1) + fib(n - 2)
  }
}
```

- テストのための構文が組み込まれている
- inspect!はスナップショットテストを行うための構文で、CLIで`moon test --update`とするとスナップショットを取る

## 構造体

```rust
struct Point {
  x : Int
  y : Int
} derive(Show)

fn main {
  // create a point
  let point = { x: 3, y: 4 }
  println("point: \{point}")
  println("point.x: \{point.x}")
  println("point.y: \{point.y}")
  
  // functional update
  let point2 = {..point, x: 20}
  println(point2)
}
```

- 構造体定義はstructで行う
- derive(show)はprintできるようにするためのもの
- フィールド名から推論するので、必ずしも型名を記述してインスタンスを作る必要はない
- 必要なら`Point::{ x: 3, y: 4 }`などとする
- フィールドはdefaultでイミュータブル
- functional updateという仕組み(スプレッド構文に似ている)で、部分更新した新しいインスタンスを作れる

## 構造体のミュータブルなフィールド

```rust
struct MutPoint {
  mut mx : Int
  y : Int
} derive(Show)

fn main {
  let point = { mx: 3, y: 4 }
  println("point: \{point}")
  point.mx = 10
  println("point: \{point}")
}
```

- 構造体のフィールドをミュータブルにすれば、フィールドの宣言時にmutとつければ良い

## 列挙型

```rust
enum Color {
  Red
  Green
  Blue
  RGB(Int, Int, Int)
  CMYK(Int, Int, Int, Int)
} derive(Show)

fn print_color(color : Color) -> Unit {
  match color {
    Red => println("Red")
    Green => println("Green")
    Blue => println("Blue")
    // Take the three Int values from RGB and print them.
    RGB(r, g, b) => println("RGB: \{r}, \{g}, \{b}")
    CMYK(c, m, y, k) => println("CMYK: \{c}, \{m}, \{y}, \{k}")
  }
}

fn main {
  let red = Red
  let green = Color::Green
  let blue = RGB(0, 0, 255)
  let black = CMYK(0, 0, 0, 100)
  print_color(red)
  print_color(green)
  print_color(blue)
  print_color(black)
}
```

- enumは、ふつうの列挙子の他に、データを関連付けた列挙子を持つことができる
- パターンマッチmatchで関連付けられたデータを変数に束縛することができる

## ニュータイプ

```rust
type UserId Int derive(Show)
type UserName String derive(Show)

fn main {
  let user_id : UserId = UserId(1)
  let user_name : UserName = UserName("Alice")
  println(user_id._)
  println(user_name._)
  // use some pattern matching to extract the values
  let UserId(id) = user_id 
  let UserName(name) = user_name
}
```

- `type newType oldType`で新しい型を作る
- 型エイリアスではなく、一つのコンストラクタを持つ列挙型と考える (元の型をwrapした新しい型)
- 中身にアクセスするには`._`を使用する
- パターンマッチを使用した変数束縛も可能

## オプション

```rust
fn first_char(s : String) -> Option[Char] {
  if s.length() == 0 {
    None
  } else {
    Some(s[0])
  }
}

fn main {
  let c1 : Char? = first_char("hello")
  let c2 : Option[Char] = first_char("")
  println("\{c1.is_empty()}, \{c1.unwrap()}")
  println("\{c2.is_empty()}, \{c2}")
}
```

- Option[T]型の変数はNoneかT型の変数をラップしたSomeのいずれかの値を持つ
- T?はOption[T]の糖衣構文
- is_empty()メソッドでNoneかどうかを判定、unwrap()メソッドでSomeの中身を取り出す
- unwrap()をNoneに対して呼び出すとpanicする

## リザルト

```rust
fn first_char(s : String) -> Result[Char, String] {
  if s.length() == 0 {
    Err("empty string")
  } else {
    Ok(s[0])
  }
}

fn main {
  let c1  = first_char("hello")
  let c2  = first_char("")
  println("\{c1.is_ok()}, \{c1}, \{c1.unwrap()}")
  println("\{c2.is_err()}, \{c2}")
}
```

- Optionだと異常系がNoneなので詳細を呼び出し元に返せない
- そこでResultではエラーメッセージを含められるようになっている
- Result[T1, T2]はT2をラップしたErrか、T1をラップしたOkのいずれかの値を持つ
- Okかを判定するis_ok()メソッドや、Okの中身を取り出すunwrap()メソッドがある
- 正しくエラーハンドリングするにはパターンマッチの使用が推奨される

## パターンマッチ

```rust
struct Point {
  x : Int
  y : Int
} derive(Show)

fn main {
  let tuple = (1, false, 3.14)
  let (a, b, c) = tuple
  println("a:\{a}, b:\{b}, c:\{c}")
  let record = { x: 5, y: 6 }
  let { x, y } = record
  println("x:\{x}, y:\{y}")
}
```

- すでに見たようにタプルや構造体を、(他の言語では)構造化束縛などと呼ばれる方法で、変数に束縛することができる

## let文におけるパターンマッチ

```rust
fn main {
  f([1, 2, 3])
  f([1, 2])
}

fn f(array : Array[Int]) -> Unit {
  let [a, b, c] = array // warning as error
  println("a:\{a}, b:\{b}, c:\{c}")
}
```

- let文でパターンマッチできるのはすでに見た通り
- 配列をパターンマッチさせようとすると、実はこのコードは動かない
- というのは、デフォルトでpartial match warningがエラー扱いになるから
- partial match warningは、完全に配列のサイズが同じでない場合にしか動かないよ、という警告

## match式におけるパターンマッチ

```rust
enum Resource {
  TextFile(String)
  Image(String)
  Folder(Map[String, Resource])
} derive(Show)

let assets : Resource = Folder(
  {
    "readme.md": TextFile("hello world"),
    "image.jpg": Image("https://someurl1"),
    "folder1": Folder(
      {
        "src1.mbt": TextFile("some code1"),
        "src2.mbt": TextFile("some MoonBit code 2"),
      },
    ),
    "folder2": Folder(
      {
        "src3.mbt": TextFile("some code3"),
        "image2.jpg": Image("https://someurl2"),
      },
    ),
  },
)

fn main {
  println("resource count: \{count(assets)}")
}

fn count(res : Resource) -> Int {
  match res {
    Folder(map) => {
      let mut sum = 0
      for name, res in map {
        sum += count(res)
      }
      sum
    }
    TextFile(_) => 1
    Image(_) => 1
  }
}
```

- match式を用いて処理を分岐することができる
- match式は上から順に見ていき、マッチした分岐に入る
- いずれにもマッチしなかった場合、プログラムが停止する
- Image(_) => 1 のようにラップした中身の価に興味がなければ、_で無視することができる
- サラッと流されているが、enumの定義は再帰できる

## 定数パターン

```rust
fn fibonacci(x : Int) -> Int {
  // assume x > 0
  match x {
    1 => 1
    2 => 2
    _ => fibonacci(x - 1) + fibonacci(x - 2)
  }
}

fn negate(x : Bool) -> Bool {
  match x {
    true => false
    false => true
  }
}

fn read(x : Char) -> Int? {
  match x {
    '1' => Some(1)
    '2' => Some(2)
    '3' => Some(3)
    _ => None
  }
}

fn contents(file : String) -> String? {
  match file {
    "README" => Some("# hello world")
    "hello.mbt" => Some("println(\"hello world\")")
    _ => None
  }
}

fn main {
  println("fib(5): \{fibonacci(5)}")
  println("negate(false): \{negate(false)}")
  println("read('2'): \{read('2')}, read('5'): \{read('5')}")
  println(contents("README"))
}
```

- 定数パターンは、マッチ式内で定数にマッチさせるパターン

## タプルパターン

```rust
fn logical_and(x : Bool, y : Bool) -> Bool {
  match (x, y) {
    (true, true) => true
    (false, _) => false
    (_, false) => false
  }
}

fn logical_or(x : Bool, y : Bool) -> Bool {
  match (x, y) {
    (true, _) => true
    (_, true) => true
    _ => false
  }
}

fn main {
  println("true and false: \{logical_and(true, false)}")
  println("true or false: \{logical_or(true, false)}")
}
```

- 複数の条件をマッチさせるには、タプルパターンを使用する
- タプルを生成するコストは発生しない (コンパイラが最適化する)

## エイリアスパターン

```rust
fn main {
  let (a, (b, _) as tuple, _) as triple = (1, (true, 5), false)
  println("a: \{a}, b: \{b}")
  println("tuple: \{tuple}")
  println("triple: \{triple}")
}
```

- 変数に束縛するとき、複数の階層で同時に束縛することができる
- `pattern as name`というのがその構文で、エイリアスパターンという

## 配列パターン

```rust
fn main {
  let array = [1, 2, 3, 4, 5, 6]
  match array {
    [a, b, ..] => println("a: \{a}, b: \{b}")
    _ => ()
  }
  match array {
    [.., c, d] => println("c: \{c}, d: \{d}")
    _ => ()
  }
  match array {
    [e,  ..,  f] => println("e: \{e}, f: \{f}")
    _ => ()
  }
  println("sum of array: \{sum(array)}")
}

fn sum(array : @array.View[Int]) -> Int {
  match array {
    [] => 0
    [x, .. xs] => x + sum(xs)
  }
}
```

- 配列のパターンマッチは、[]を用いて行う
- ..で「のこり」の部分を指定する
- ..の「のこり」の部分にはエイリアスをつけることができる

## Orパターン

```rust
enum Color {
  Blue
  Red
  Green
  RGB(Int, Int, Int)
  RGBA(Int, Int, Int, Int)
} derive(Show)

fn get_green(color : Color) -> Int {
  match color {
    Blue | Red => 0
    Green => 255
    RGB(_, g, _) | RGBA(_, g, _, _) => g
  }
}

fn main {
  println("The green part of Red is \{get_green(Red)}")
  println("The green part of Green is \{get_green(Green)}")
  println("The green part of Blue is \{get_green(Blue)}")
  println("The green part of RGB(0,0,0) is \{get_green(RGB(0,0,0))}")
  println("The green part of RGBA(50,5,0,6) is \{get_green(RGBA(50,5,0,6))}")
}
```

- |で区切ることで、「または」の条件でパターンマッチさせることができる
- 同じ型で同じ名前に限り、変数束縛も可能

## Rangeパターン

```rust
fn score_to_grade(score : Int) -> String {
  match score {
    0..<60 => "F"
    60..<70 => "D"
    70..<80 => "C"
    80..<90 => "B"
    90..=100 => "A"
    _ => "Invalid score"
  }
}

fn classify_char(c : Char) -> String {
  match c {
    'A'..='Z' => "UpperCase"
    'a'..='z' => "LowerCase"
    '0'..='9' => "Digit"
    _ => "Special"
  }
}

fn is_scalar_value(codepoint : Int) -> Bool {
  match codepoint {
    0x0000..=0xD7FF | 0xE000..=0x10FFFF => true
    _ => false
  }
}

fn main {
  println(score_to_grade(50))
  println(score_to_grade(85))
  println(score_to_grade(95))
  println(classify_char('A'))
  println(classify_char('1'))
  println(classify_char('!'))
  println(is_scalar_value(0xD500))
}
```

- 整数のシーケンスにマッチさせる場合には、Rangeパターンが使える
- Byte, Int, UInt, Int64, UInt64, Charなどの整数型に限る

## イミュータブルリスト

```rust
fn count[A](list : @immut/list.T[A]) -> UInt {
  match list {
    Nil => 0
    Cons(_, rest) => count(rest) + 1 // may overflow
  }
}

///|
fn main {
  let empty_list : @immut/list.T[Int] = Nil
  let list_1 = @immut/list.Cons(1, empty_list)
  let list_2 = @immut/list.Cons(2, list_1)
  let list_3 = @immut/list.Cons(3, empty_list)
  let reversed_1 = list_1.rev()
  let n = count(reversed_1)
}
```

- `@immut/list`パッケージの型
- NilまたはCons(最初の要素と残り)のいずれかの値を持つ
- サラッとジェネリクスが例で使われている。`fn func[T]`でジェネリックな関数定義となる

## イミュータブルセット

```rust
///|
fn main {
  let a = @immut/hashset.of([5, 4, 3, 2, 1])
  let b = @immut/sorted_set.of([5, 4, 3, 2, 1])
  let arraya = a.iter().collect()
  let arrayb = b.iter().collect()
  let c = a.add(6)
  let d = b.add(6)
  let except_one = d.remove_min()
  println(d)
}
```

- `@immut/hashset`または`@immut/sorted_set`の型
- hashsetはハッシュを使ったセットなので、順序は関係ない
- sorted_setは木構造を使ったセットで、整列されている
- C++ならunordered_setとsetに相当

## イミュータブルマップ

```rust
///|
fn main {
  let a = @immut/hashmap.of([(1, "a"), (2, "b"), (3, "c")])
  let b = a.add(4, "d")
  let c = @immut/sorted_map.from_iter(b.iter())
  println(c.keys())
}
```

- セットと同様

## メソッド

```rust
type MyInt Int

fn increment(self : MyInt) -> MyInt {
  MyInt(self._ + 1)
}

fn MyInt::println(x : MyInt) -> Unit {
  println("MyInt(\{x._})")
}

fn main {
  let x = MyInt(39)
  let y = x.increment() // call method via dot syntax
  let z = increment(y) // `fn increment(..)` can be called directly
  let w = MyInt::increment(z) // call methods using qualified syntax
  w.println()
  MyInt::println(w) // `fn MyInt::println` cannot be called via `println(..)` directly
}
```

- すでにメソッドは出てきたが、メソッドの定義は2通りある
- 一つはトップレベル関数として定義し、オブジェクトを第一引数selfという名前で受け取る方法
- もう一つは`fn T::method(x: T)`と限定子付きの関数として定義し、オブジェクトを第一引数で受け取る方法(この場合名前は何でも良い)
- 最初の方法で定義した場合は、直接呼び出しが可能になる
- だが、トップレベル関数で名前の衝突は許可されていないので、たとえば、T1::method()とT2::method()を両方定義したいのであれば、2つめの方法で限定子付きの関数として定義するしかない

## 感想

とりあえず、現時点で言語のチュートリアルはここまで。
チュートリアルは言語機能をカバーできておらず、特にパッケージや参照など気になるトピックは残っているものの、ここまでの感想としては、概ねC系の言語をやってきた人ならとっつきやすいのではないかと思いました。
パターンマッチなどの機能は最近の言語にはよく見られるもので、クセの少ない言語だと思います。

# Tour for Beginners

次にこちらのTour for Beginnersをやります。

https://docs.moonbitlang.com/en/latest/tutorial/tour.html

## インストール

VSCodeの拡張機能からインストールすることが推奨されています。

https://marketplace.visualstudio.com/items?itemName=moonbit.moonbit-lang&ssr=false#overview

こちらから、拡張機能をインストールして、WSLのワークスペースを開いたうえで、Ctrl-Shift-Pでコマンドパレットを開き、"Moonbit: Install moonbit toolchain"を指定すると、インストールスクリプトをダウンロードして実行し始めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419261/96c873dc-62a4-4d67-a397-ff525606e24f.png)


```
 *  実行するタスク: curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash -s '0.1.20250529+8a98c8e02' 

Downloading moonbit ...
######################################################################## 100.0%
Downloading core ...
######################################################################## 100.0%
Bundling core ...
Warning: [0029]
   ╭─[/home/hokazono/.moon/lib/core/bigint/moon.pkg.json:4:5]
   │
 4 │     "moonbitlang/core/char",
   │     ───────────┬───────────  
   │                ╰───────────── Warning: Unused package 'moonbitlang/core/char'
───╯
[======================================- ] 55/57 done, 1/1 running
Finished. moon: ran 57 tasks, now up to date
[=======================================-] 56/57 done, 1/1 running
Finished. moon: ran 57 tasks, now up to date
moonbit was installed successfully to ~/.moon 
To verify the downloaded binaries, check https://www.moonbitlang.com/download#verifying-binaries for instructions.
To know how to add shell completions, run 'moon shell-completion --help'
Added "~/.moon/bin" to $PATH in "~/.bashrc" 
To get started, run: 
  source ~/.bashrc 
  moon help 
 *  任意のキーを押してターミナルを終了します。 
```

インストールが終わったようです。`${HOME}/.moon`に関連するファイルが配置されるようです。

```bash
$ tree ~/.moon -L 2
/home/hokazono/.moon
├── bin
│   ├── internal
│   ├── lsp-server.js
│   ├── moon
│   ├── moonc
│   ├── mooncake
│   ├── moon_cove_report
│   ├── moondoc
│   ├── moonfmt
│   ├── mooninfo
│   ├── moonlex.wasm
│   ├── moonrun
│   └── moonyacc.wasm
├── include
│   ├── moonbit-fundamental.h
│   └── moonbit.h
└── lib
    ├── core
    ├── libmoonbitrun.o
    ├── libtcc1.a
    ├── runmain.o
    ├── runtime.c
    └── runtime_core.c
```

また、`bashrc`には`$HOME/.moon/bin`をPATHに追加する文が追加されています。

```bash
$ tail -2 ~/.bashrc
# moonbit
export PATH="$HOME/.moon/bin:$PATH"
```

さっそく、`moon`コマンドを叩いてみます。

```bash
$ moon
The build system and package manager for MoonBit.

Usage: moon [OPTIONS] <COMMAND>

Commands:
  new                    Create a new MoonBit module
  build                  Build the current package
  check                  Check the current package, but don't build object files
  run                    Run a main package
  test                   Test the current package
  clean                  Remove the target directory
  fmt                    Format source code
  doc                    Generate documentation
  info                   Generate public interface (`.mbti`) files for all packages in the module
  bench                  Run benchmarks in the current package
  add                    Add a dependency
  remove                 Remove a dependency
  install                Install dependencies
  tree                   Display the dependency tree
  login                  Log in to your account
  register               Register an account at mooncakes.io
  publish                Publish the current module
  package                Package the current module
  update                 Update the package registry index
  coverage               Code coverage utilities
  generate-build-matrix  Generate build matrix for benchmarking (legacy feature)
  upgrade                Upgrade toolchains
  shell-completion       Generate shell completion for bash/elvish/fish/pwsh/zsh to stdout
  version                Print version information and exit
  help                   Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help

Common Options:
  -C, --directory <SOURCE_DIR>   The source code directory. Defaults to the current directory
      --target-dir <TARGET_DIR>  The target directory. Defaults to `source_dir/target`
  -q, --quiet                    Suppress output
  -v, --verbose                  Increase verbosity
      --trace                    Trace the execution of the program
      --dry-run                  Do not actually run the command
      --build-graph              Generate build graph
```

ちゃんとインストールされているようですね。

さっそく、`moon new`コマンドを使用して、プロジェクトを作成してみます。

```bash
$ moon new
Enter the path to create the project (. for current directory): .
Select the create mode: exec
Enter your username: hokacci
Enter your project name: sputnik
Enter your license: No License
Initialized empty Git repository in /home/hokazono/repos/sputnik/.git/
Created .
```

こんな感じで作られました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419261/3db76c32-f775-47c0-9652-5c2041fac6b2.png)


## Exampleプロジェクト

複数の生徒のスコアが入力され、合格した生徒の数を出力する関数を実装します。

次のようなコードを追加してみました。

### src/lib/top.mbt

```rust
// 生徒
pub(all) struct Student {
  id : String
  score : Double
} derive(Show)


// 試験結果 (合格/不合格)
pub(all) enum ExamResult {
  Pass
  Fail
} derive(Eq, Show)


// 合格基準を満たすかどうかを判定する関数
pub fn is_qualified(student : Student, criteria: Double) -> ExamResult {
  if student.score >= criteria {
    Pass
  } else {
    Fail
  }
}


// 合格者の数を数える関数
pub fn count_qualified_students(
  students : Array[Student],
  is_qualified : (Student) -> ExamResult
) -> Int {
  let mut count = 0
  for student in students {
    count += match is_qualified(student) {
      Pass => 1
      Fail => 0
    }
  }
  count
}
```

### src/lib/top_test.mbt

```rust
test "is qualified" {
  assert_eq!(is_qualified(Student::{ id : "0", score : 50.0 }, 60.0), Fail)
  assert_eq!(is_qualified(Student::{ id : "1", score : 60.0 }, 60.0), Pass)
  assert_eq!(is_qualified(Student::{ id : "2", score : 13.0 }, 7.0), Pass)
}


test "count qualified students" {
  let students: Array[Student] = [
    { id: "0", score: 10.0 },
    { id: "1", score: 50.0 },
    { id: "2", score: 61.0 },
  ]
  let criteria1 = fn(student) { is_qualified(student, 10) }
  let criteria2 = fn(student) { is_qualified(student, 50) }
  assert_eq!(count_qualified_students(students, criteria1), 3)
  assert_eq!(count_qualified_students(students, criteria2), 2)
}
```

### テスト

テストを実行すると、もともとあったテストに加えて成功しているようです。
やった！

```bash
$ moon test
Total tests: 3, passed: 3, failed: 0
```

# 感想

公式のチュートリアルに沿って、言語機能を軽く見て、インストールしてExampleプロジェクトを動かしました。

今回のチュートリアルでは、WebAssemblyがベースになっている利点を感じることはできなかったので、また今度ブラウザから今回作ったモジュールを呼び出すとかしてみたいと思いました。