はじめに#

MoonBit プログラムは、次のようなトップレベル定義から構成されます:

  • 型定義

  • 関数定義

  • 定数定義と変数束縛

  • init 関数、main 関数、test ブロック

式と文#

MoonBit では文と式を区別します。関数本体では最後の句だけが式であり、それが戻り値になります。例えば:

fn foo() -> Int {
  let x = 1
  x + 1
}

fn bar() -> Int {
  let x = 1
  //! x + 1
  x + 2
}

式には次のものが含まれます:

  • 値リテラル(例:真偽値、数値、文字、文字列、配列、タプル、構造体)

  • 算術演算、論理演算、比較演算

  • 配列要素(例:a[0])、構造体フィールド(例:r.x)、タプル要素(例:t.0)などへのアクセス

  • 変数と(先頭が大文字の)enum コンストラクタ

  • 無名ローカル関数定義

  • matchifloop 式など

文には次のものが含まれます:

  • 名前付きローカル関数定義

  • ローカル変数束縛

  • 代入

  • return

  • 戻り値の型が Unit の式(例:ignore

コードブロックには複数の文と 1 つの式を含められ、その式の値がコードブロックの値になります。

変数束縛#

変数は let mut または let を使って、それぞれ可変または不変として宣言できます。可変変数には新しい値を再代入できますが、不変変数にはできません。

定数はトップレベルでのみ宣言でき、変更できません。

let zero = 0

const ZERO = 0

fn main {
  //! const ZERO = 0 
  let mut i = 10
  i = 20
  println(i + zero + ZERO)
}

注釈

トップレベルの変数束縛は、

  • 明示的な型注釈が必要です(文字列、バイト、数値などのリテラルで定義する場合を除く)

  • 可変にはできません(代わりに Ref を使ってください)

命名規則#

変数名と関数名は小文字の a-z で始める必要があり、文字、数字、アンダースコア、その他の非 ASCII Unicode 文字を含められます。snake_case を使うことを推奨します。

定数名と型名は大文字の A-Z で始める必要があり、文字、数字、アンダースコア、その他の非 ASCII Unicode 文字を含められます。PascalCase または SCREAMING_SNAKE_CASE を使うことを推奨します。

キーワード#

以下はキーワードであり、識別子として使用できません:

[
  "as", "else", "extern", "fn", "fnalias", "if", "let", "const", "match", "using",
  "mut", "type", "typealias", "struct", "enum", "extenum", "trait",
  "traitalias", "derive", "while", "break", "continue", "import", "return",
  "throw", "raise", "try", "catch", "pub", "priv", "proof_assert", "proof_let",
  "readonly", "true", "false", "_", "test", "loop", "for", "in", "impl", "with",
  "guard", "async", "is", "suberror", "and", "letrec", "enumview", "noraise",
  "defer", "lexmatch", "where", "declare", "nobreak",
]

予約語#

以下は予約語です。使用すると警告が出ます。将来的にキーワードになる可能性があります。

[
  "module", "move", "ref", "static", "super", "unsafe", "use", "await",
  "dyn", "abstract", "do", "final", "macro", "override", "typeof", "virtual", "yield",
  "local", "method", "alias", "assert", "package", "recur", "using", "enumview",
  "isnot", "define", "downcast", "inherit", "member", "namespace", "static", "upcast",
  "use", "void", "lazy", "include", "mixin", "protected", "sealed", "constructor",
  "atomic", "volatile", "anyframe", "anytype", "asm", "await", "comptime", "errdefer",
  "export", "opaque", "orelse", "resume", "threadlocal", "unreachable", "dynclass",
  "dynobj", "dynrec", "var", "finally", "noasync", "assume",
]

プログラムのエントリ#

initmain#

init 関数という特別な関数があります。init 関数には次の特徴があります:

  1. 引数リストも戻り値型も持ちません。

  2. 同じパッケージ内に複数の init 関数を定義できます。

  3. init 関数は、他の関数から明示的に呼び出したり参照したりできません。代わりに、パッケージ初期化時にすべての init 関数が暗黙的に呼び出されます。したがって、init 関数は文のみで構成する必要があります。

fn init {
  let x = 1
  println(x)
}

main 関数という別の特別な関数もあります。main 関数はプログラムの主エントリであり、初期化段階の後に実行されます。

init 関数と同様に、引数リストも戻り値型も持ちません。

fn main {
  let x = 2
  println(x)
}

前の 2 つのコード例は、実行時に次を出力します:

1
2

このような main 関数を定義できるのは main パッケージだけです。詳しくは ビルドシステムチュートリアル を参照してください。現在のプロジェクトでは、これは moon.pkg で次のように設定します:

moon.pkg#
options(
  "is-main": true,
)

test#

test ブロックというトップレベル構造もあります。test ブロックでは次のようにインラインテストを定義できます:

test "test_name" {
  assert_eq(1 + 1, 2)
  assert_eq(2 + 2, 4)
  debug_inspect([1, 2, 3], content="[1, 2, 3]")
}

以降では test ブロックと main 関数を使って実行結果を示します。特に断りがない限り、すべての test ブロックは成功すると仮定します。