Zigのcomptimeのどこが良いのか?

2022/12/13に公開

これはZig Advent Calendar 2022 13日目の記事です。
https://qiita.com/advent-calendar/2022/ziglang

Zigの言語機能の中でcomptimeに興奮している人をしばしば見かけます。
https://twitter.com/jarredsumner/status/1457630795695198208

何を隠そう僕もその中の一人なのです。
しかし傍から見ると何にそれほど興奮しているのか分からない機能ではあると思います。

なので僕が個人的にcomptimeの何が良いと思っているかを書こうと思います。

comptimeとは何か?

コンパイル時に既知の式であるかを示すキーワードでcomptimeで修飾された式はコンパイル時に既知でないといけません。

const n = comptime factorial(5);// コンパイル時に決定される(計算される)必要がある

https://ziglang.org/documentation/master/#comptime

とても単純な機能ですがZigではcomptimeによって多くのことが実現されています。

// コンパイル時計算
const n_max = comptime factorial(5);

// メタプログラミング
fn add(comptime T: type, a: T, b: T) T {
    return a + b;
}

fn Vector2(comptime T: type) type {
    return struct {
        x: T,
        y: T,
    };
}

// コンパイル時の条件分岐
fn debugOnlyFunc() void {
    if (comptime builtin.mode != .Debug) {
        @compileError("debug mode only");
    }
    ...
}

fn SparseSet(comptime T: type) type {
    // コンパイル時に受け取った型の要件をチェック
    if (comptime std.meta.trait.isUnsignedInt(T) == false) {
        @compileError("SparseSet is Int only.");
    }
    ...
}

何が嬉しいのか?

1つの機能で多くのことを実現できるから良いのか?
そうではなく「コンパイル時に決定する」という事を明示することでコードを単純に保ちつつ多くの機能を実現できるからcomptimeは優れていると感じています。

コンパイルというプロセスを行う言語はすべての機能が暗黙的にコンパイル時の動作を内包していると言えます。

その中でコンパイル時に決定するという事を明示的に扱えない言語でコンパイル時に処理されるコードを書く際は、どの機能がコンパイル時に展開されるのか?どう展開されるのか?という暗黙的な動作を頭に入れて書く必要があります。
それに対してcomptimeというキーワードを持つZigではコンパイル時に決定される事を明示的に示すことができるので暗黙的な動作を意識することなく通常のコードを処理するのと同じように記述することができます。

機能の中に暗黙的に含まれている「コンパイル時に処理されるか否か」を独立したキーワードとして切り出すことで、コンパイル時のコード展開処理を明文化し整理している点がcomptimeの優れた点なのです。

あとがき

単なるGenericsと見ると穴(conceptのように制約をかける手段がないなど)は割と有りますがコンパイル時という概念を明示的に扱えるというのが評価ポイントということでここはひとつ。

まあVer1.0にヒットするころには何かしら制約を掛ける機能が入るでしょう。
入ってくれ無いと困る。

Discussion