ファイルを書く
エディタを開き、次の 5 行を hello.0 として保存してください(あるいは下のブロックの「Run」をクリックするだけでもかまいません)。
これだけで Zero プログラムは完成です。ローカルで実行するには次のコマンドを使います。
zero run hello.0
次のように表示されるはずです。
hello from zero
command not found と出た場合は、Zero のインストール に戻り、まず zero --version が動くことを確認してください。
すべてのトークンを解きほぐす
この小さなプログラムだけでも、Zero らしさのほとんどが顔を出しています。左から右へ順に読んでいきましょう。
pub
pub は宣言を public にする——現在のモジュールの外から見えるようにします。ランタイムはファイルのスコープの外から main を見つける必要があるので、main は pub でなければなりません。同じファイル内のプライベートなヘルパー関数なら、pub は不要です。
fun
fun は関数宣言を導入します。Zero は fn(Rust)でも、func(Go)でも、function(JavaScript)でもなく fun を使います。キーワードはひとつだけ、常に同じ書き方です。
main
慣習上のエントリーポイント名です。Zero の実行可能ターゲットを動かすと、ランタイムは pub fun main(world: World) を探して呼び出します。他の関数の名前は自由ですが、main はプログラムのエントリ用に予約されています。
(world: World)
唯一のパラメータの名前は world、型は World です。ランタイムは main を呼ぶ前に World 値を組み立て、それをここに渡します。この値がプログラムの ケイパビリティ を保持します——標準出力、標準入力、ファイルシステム、ネットワークなど、ランタイムが許可するものへのアクセスです。
パラメータ名は自由に選べます。(w: World) でも (io: World) でもコンパイルは通ります。サンプルでは world が慣例なので、ここでもそれに倣います。
-> Void
戻り値の型です。Void は「この関数は有用な値を返さない」という意味——副作用のために存在し、値を返すためのものではありません。実際に書く関数の多くは、i32 や自分で定義した shape のような、より具体的な型を返します。
raises
エラー型を伴わない素の raises を main に付けると、「この関数は失敗しうる」という意味になります。main の場合、本体内の check がエラーを伝播させれば、プログラムは非ゼロのステータスで終了する、というわけです。より絞り込んだ形——たとえば raises { InvalidInput }——については Raises と Check で扱います。
check world.out.write("hello from zero\n")
実際に何かを行っているのはこの行です。3 つに分けて見てみましょう。
world.out—Worldケイパビリティ上のフィールドとして公開されている、標準出力ストリーム。.write("hello from zero\n")— そのストリームに文字列を書き込むメソッド。失敗の可能性がある結果を返します(書き込みが失敗するかもしれない、ストリームが閉じているかもしれない、など)。check— 失敗した場合にエラーを上に伝播させます。checkがないと、コンパイラーはwriteの結果が黙って捨てられている、と文句を言ってきます。
末尾の \n はリテラルな改行です。これがないと出力が改行されず、シェルプロンプトがメッセージと同じ行に出てきてしまいます。
いま何が起きたのか
zero run hello.0 を実行したとき、内部では次のことが起きていました。
- コンパイラーがファイルをパースして型検査する。
- プラットフォーム向けの小さなネイティブ実行ファイルを生成する。
- ランタイムが現在のプロセス用に
Worldケイパビリティを構築する。 main(world)を呼び出す。- あなたのコードが
world.out(端末の標準出力にひも付けられている)に"hello from zero\n"を書き込む。 mainがVoidを返し、ランタイムが後片付けをして、プログラムはステータス 0 で終了する。
ガベージコレクターも、隠れたランタイム初期化も、暗黙のモジュールブートストラップもありません。プログラム全体は、あなたが書いた関数と、そこから呼び出された標準ライブラリのコードだけです。
ちょっと変えてみる
メッセージを書く前に、いったん束縛してみましょう。
もう一度実行してみてください。出力は同じですが、これで let バインディング を体験し、文字列が受け渡しできる第一級の値だということを確認できました。
失敗するバリアント
check を書き忘れるとどうなるでしょうか。
pub fun main(world: World) -> Void raises {
world.out.write("oops\n")
}
zero check hello.0 はこれをコンパイルしません。write の結果は失敗しうる値で、それを無視するのはコンパイルエラーです。check で伝播させるか、明示的にハンドリングするか、どちらかが必要です。これは Rust の must_use と同じ発想ですが、すべての失敗しうる呼び出しに対して強制される点が異なります。
次回: Zero の CLI
ここでは zero run を使いました。CLI には覚えておくと便利なコマンドが少数あります——check、run、build、test、fix、explain など。それぞれにエージェント向けの構造化された --json モードが用意されています。
よくある質問
Zero の hello world プログラムは?
定番の Zero hello world はわずか 5 行です。pub fun main(world: World) -> Void raises { check world.out.write("hello from zero\n") }。hello.0 として保存し、zero run hello.0 で実行します。
Zero の pub fun main の意味は?
pub fun main の意味は?pub は宣言を public にする——モジュールの外から見えるようにするキーワードです。fun は関数を宣言します。main は Zero が実行可能ターゲットのエントリーポイントとして探す、慣習上の関数名です。あわせて pub fun main で、ランタイムが起動時に呼び出す public なエントリーポイントを宣言したことになります。
なぜ main は World パラメータを取るのですか?
World パラメータを取るのですか?Zero にはグローバルな I/O が存在しません。標準出力、標準入力、ファイル、ネットワークなど、外の世界とやり取りするすべての操作は、明示的に渡されたケイパビリティ経由で行われます。ランタイムは main に World 値を渡し、関数が I/O できるのはその値(あるいはその一部)を介してのみ。これにより副作用が関数のシグネチャに可視化されます。
hello world での raises と check の役割は?
raises と check の役割は?main の raises は「この関数は失敗しうる」と宣言します。check world.out.write(...) は失敗しうる関数を呼び、失敗時はそのエラーを呼び出し元——ここではランタイム——へ伝播させ、非ゼロのステータスで終了します。check がなければ、エラーが未処理のままになるため、コンパイラーは呼び出しのコンパイル自体を拒否します。
Zero のソースファイルの拡張子は?
Zero のソースファイルは .0 拡張子を使います(アルファベットの O ではなく、数字のゼロです)。hello.0 は Zero のソースファイルです。コンパイラーは zero check hello.0、zero run hello.0 のようなコマンドで呼び出します。