最近 Zig でコードを書く時間が増えており、心の潤いになっています。この記事では、Zig における抽象化についての知見を紹介します。著者が有用だと感じたものの、広くはあまり使われていない技法も取り上げています。
Zig のインターフェースについては多くが書かれてきましたが、それは当然のことです。インターフェースは言語機能ではありませんが、Zig で実行時多相性を実現する事実上の手段となっています。また、Zig の comptime をどう活用すれば、動的ディスパッチの性能問題を避け、洗練されたデータ指向のコードを書けるかについても多く語られてきました。
Zig における多相性は、通常二つの方法で得られます。動的インターフェースは、不透明ポインタ、仮想テーブル、動的ディスパッチを使って実行時多相性を実現します。コンパイル時のダックタイピングは、型のリフレクション、switch 文、コンパイル時のモノモーフィズムを使って静的多相性を実現します。この状況は効果的であることが証明されてきましたが、トレードオフがないわけではありません。インターフェースは他言語でおなじみの型契約を提供しますが、実行時にモジュール化されるため、コンパイラにとって硬い障壁となり、その結果性能のオーバーヘッドが生じます。性能が重要なコードでは、Zig は通常コンパイル時のダックタイピングに頼ります。これはインターフェース違反をコンパイル時に検出しますが、契約を慎重に文書化する必要は依然としてあります。
Rust の trait や Haskell の型クラスから来た開発者は、この仕組みを単純すぎると感じるかもしれません。そして Zig は確かにシンプルさを目指しています。著者は、少しの工夫によって Zig のユーザー空間でも、型に対する同様の抽象化が可能だと主張します。提案は、コンパイル時の型契約をTraitとして定義する新しい規約であり、ライブラリや追加の仕組みは不要です。関数型プログラミル全般、特に OCaml に着想を得ています。
Trait は、特定の型に関連する関数を含むコンパイル時の構造体として定義されます。記事では、畳み込みのための Reducer trait でこれを説明しています。そこでは、ある型が空の値と結合操作のもとでリデューサーを形成します。i32 向けに分けた sum と product の trait は、同じ汎用の reduce ロジックを再利用できることを示し、コンパイラがループをインライン化し展開できることを示しています。著者は結論として、trait とは単に楽しさの詰まった袋にすぎない、と述べています。
出典: Hacker News · 要約:HeadlinesBriefing