Zigでなんかやり始めたの8月下旬わず。それまで新しいプログラミング言語なんかあんま触れようとしてこなかった。やってきたのはCとPythonとPerlとVanilla JSとシェルスクリプトであり、なんか他にやるようなことはあるのかという感じだった。PCに処理系をインストールしたらインストールサイズがでかくてディスクのスペースとりそうでマジで机上の空論だけで済まそうとしてた。だがZigが現代のC言語だのという論説とか、実際にインストールしたらマジで本体としては小さくてマジですごいと思った。んでプログラミング言語Zigのすごいところの論説で、気がつけばZigを始めてた。
私
たまたま学校でエンジニアリングを学んできたがたまたま私はエンジニアと縁がなかったしこれからもないだろうなとは今もなお思ってるし、どうせ死ぬだろうなとは思ってるし、プログラマか何かにこだわるのが馬鹿馬鹿しいと考えている、何故かプログラミングが趣味になってしまった製造業現場職の者。2015年から学校で教わったのを皮切りに今年で10年目。と数えて意味があるのか。
最近作ってるプロジェクト
私はEsolangの者わず。コードゴルフ参加のため、プログラミング言語Disの処理系を作ってた。
処理系を作るだけならBen Olmsteadさんの二番煎じ。しかし私は最適化実行の機能とか、プログラミング言語Disに関するライブラリとか、あとなんやかんやを実装しまくって実装することを楽しんでたんだと思う。
んで、Zigの存在を知って、CとJavaScriptで作ってた2つの実装をZigでやり直そうかなとか思えてきて、プロジェクトとしてスタートしちゃった。
作ろうとしてる処理系の特徴
最適化レベルの指定ができるやつとか任意の進数や任意の桁のデータを取り扱うタイプのバリアントとか。なんかめちゃくちゃgeneralizationしちゃってるやつ。あと既存のコードを一ミリも活用しようとしてないやつ。
本題
最初私はこういう構造体を作りたかった。
pub fn Vm(comptime Math: type) type {
return struct {
a: Math.T,
c: Math.T,
d: Math.T,
mem: [Math.END]Math.T,
reader: anytype,
weiter: anytype,
};
}
どうせおもちゃのようなEsolangの実装であって第三者が私のライブラリを使うとかマジでありそうもないんだし、readerとwriterを素直に標準入出力にすればよいのに私はこのフィールドを一般化しようとしてた。このフィールドはプログラミング言語Disの処理系において入出力に関わるやつであり処理系の本体の外で良い。
しかしうまくいかなかった。anytypeは関数のパラメータの型でしか指定できない。
一方*const anyopaque
とかanyopaque
も試した。これは標準ライブラリにおける使用例を見たうえでの試みわず。しかしopaque
型は内容が公開されていない構造体に使うやつであり、そのせいでReaderやWriterのメソッド等が利用できなくて詰んだ。
あと最初
pub fn Vm(
comptime Math: type,
comptime reader: anytype,
comptime writer: anytype
) type {
// 略
}
も前はやってた。だがテストコード作成時、varなreader等でテストしたくてテストしたら、アレだった。型を獲る関数は全てコンパイル時に得られなければならないが、reader等がvarなら終わってた。
したがって、私が一度たどり着いたのはreader等を構造体から外し、コンパイルされたプログラムについて一ステップだけ処理を行うメソッドにreader等を引数として与えるデザインだった。
var vm = Vm(MyMath).init();
vm.run(my_reader, my_writer);
納得のいく解決策は灯台下暗し
このことをとあるDiscordサーバで話した。
すると一人が私にこうリプライした。
reader.any()
によりstd.io.AnyReader等を取得することでその方によるフィールドが定義できるはずだと。
struct {
reader: std.io.AnyReader,
writer: std.io.AnyWriter,
}
std.io.AnyReader等はstd.io.GenericReader等と比べて新しいAPIであり、これまでわりとバラバラだったReader群の型?を統一してくれる。
まとめ:std.io.AnyReader等の恩恵
ということで私はあのコードを変えようと思う。