値に名前を付ける方法はひとつ
Zero では、let で値に名前を付けます。
let answer = 42
これで構文の全部です。var も const も auto もありません。バインディングキーワードがひとつだけ——エージェントも人間も、一度学んでどこでも適用できます。
let は現在のスコープにローカルバインディングを導入します。この行のあと、answer は囲むブロックの終わりまで 42 を指します。
型推論
コンパイラーは右辺から型を推論します。リテラル 42 はデフォルトで i32 型なので、answer は i32 です。リテラル "hello" は文字列なので、
let greeting = "hello"
は greeting を文字列値に束縛します。Pair<i32, u8> を返す関数を呼ぶと、バインディングの型は Pair<i32, u8> になります。
let pair = makePair(40, 2_u8)
すべてのバインディングに型を書く必要がないので、コードは読みやすく保てます。
明示的な型注釈
型を文書化したいとき——あるいは推論が別の型を選んでしまうところで特定の型を強制したいとき——コロンのあとに型を書きます。
let count: u8 = 10
let pair: Pair<i32, u8> = makePair(40, 2_u8)
注釈は、リテラルが複数の型になりうるときに、コンパイラーへのヒントにもなります。リテラル 10 は i32、i64、u8 などになり得ます。注釈はそれを確定させます。
リテラルに型サフィックスを付けて、バインディングへの注釈の代わりにすることもできます。
let small = 10_u8 // リテラルサフィックスから u8
let big = 10_i64 // リテラルサフィックスから i64
どちらの形も有効です。呼び出し箇所で意図がよりはっきりする方を選びましょう。
バインディングを動かしてみる
推論と明示形を両方使う動作例——Run をクリックしてどうぞ。
point は右辺が構造体リテラルなので明示的に注釈しています。total は推論——sum は i32 を返すと宣言されているので、バインディングも i32 です。
スコープとシャドーイング
let バインディングは、それを宣言する行から、囲むブロックの終わりまで有効です。ネストされたブロックは新しいスコープを作ります。
pub fun main(world: World) -> Void raises {
let value = 1
if true {
let value = 2 // このブロック内で外側の 'value' をシャドーする
// ここでは value == 2
}
// if の外に戻ると、再び value == 1
}
内側の value は外側を変更しません——別のバインディングで、if ブロックの閉じ括弧でスコープから外れます。Rust や ML 系言語と同じモデルです。一連のステップを通じて値を変換するときに、中間結果のたびに新しい名前を発明しなくて済む、というのが特にありがたい場面です。
let が しない こと
他の言語からの期待が裏切られる、let があえて含まないものをいくつか挙げます。
- 型だけの宣言。 未初期化のバインディングを導入する
let x: i32;の形はありません。バインディングは宣言時に必ず値を持ちます。 - パターン分解(まだ)。
let (a, b) = pairのように書ける言語もありますが、Zero は意図的に小さく、現時点では素の名前バインディングに集中しています——分解構文が入ったかは現行ドキュメントで確認してください。 - 異なる寿命のための複数キーワード。 別の
static、const、let mut、ブロックスコープ vs 関数スコープのバリアントはありません。キーワードはひとつ。
言語のバックグラウンドが JavaScript なら、最も近い類似は const です——ブロックの残りで値に束縛された名前で、内側のスコープでシャドーイングできるもの。Rust のバックグラウンドなら、ここでの let は明示的な mut キーワードを除いた Rust の let と同じ役割です。
パターン: 値をステップごとに組み立てる
バインディングは、計算を一連の名前付き中間ステップとして書きたいときに輝きます。これは人間が読むときにも、エージェントが各行をローカルに推論するときにも有用です。
各行は、残りの関数が使うひとつの新しい事実を導入します。中間値に名前を付けてもコンパイラーは引き締まったコードを生成します——名前付けにランタイムコストはありません。
次回: プリミティブ型
let は束縛するものがなければあまり意味がありません。次のドキュメントでは Zero の プリミティブ型 ——よく目にする整数の幅、浮動小数点、文字列、Void、Bool 型——を見ていきます。
よくある質問
Zero で変数を宣言するには?
let を使います。形は、推論型なら let 名前 = 値、明示的に型を書くなら let 名前: 型 = 値 です。例: let answer = 42 または let answer: i32 = 42。どちらも現在のスコープで answer を 42 に束縛します。
Zero は let バインディングの型を推論しますか?
はい。let total = sum(point) と書き、sum が i32 を返すなら、バインディングの型は i32 と推論されます。型を文書化したいときや特定の型を強制したいときは明示的に注釈できます——たとえば let count: u8 = 10 のように。
Zero の let バインディングは可変ですか?
素の let はスコープ内で使うローカルバインディングを導入します。pre-1.0 Zero における可変性の話はまだ進化中です——言語は明示的なエフェクトと予測可能なメモリを重視するため、バインディング越しに状態を変える操作はそれが見えるようにする必要があります。お使いのツールチェーンの正確な可変性構文は現行の Zero ドキュメントで確認してください。
Zero の let と const の違いは?
Zero は関数本体内の通常のローカルバインディングに let を使います。JavaScript の let/const/var のように複数のバインディングキーワードを公開しません——表面を小さく保つのは意図的な設計判断です。コンパイル時定数は通常、別キーワードではなく型システムまたはトップレベル宣言で表現します。
Zero では let バインディングを再宣言できますか?
バインディングは囲むスコープ内にだけ生きます。ネストされたスコープでの同名の新しい let は別のバインディングで、内側のスコープが続く間だけ外側を隠します——内側のスコープが終われば外側のバインディングは影響を受けません。Rust や ML 系言語と同じモデルです。