Denoで困ったときはZigで何とかなるかもしれない
サードパーティライブラリが無いよ問題
いきなりネガティブな話で申し訳ございません。愚痴みたいなものです。本題ではありませんので読み飛ばしていただいて問題ありません。
Denoユーザーの1人としての個人的な印象として、Denoの勢いは2023年に入った頃から低迷しているように感じます。2022年末からnpm対応によってNode.jsの資産を使えるようになったにも関わらずです。実際のユーザー数はDenoチームではないので分かりませんが、登場から数年経って真新しさも無くなったので、どんなサービスにもありがちなことなのかなと思います。
ありがちとはいえ、ただ低迷しているだけではありません。ここからNode.jsを超えるのはなかなか難しいです。なぜならnpm対応というのは、明示的にNode.jsに対して「生き残っていい」それどころか「あなたが正しい」と宣言してしまったようなものです。Node.jsの正式後継の立場だったはずが、Bunと同じ立場になって後継になれたらいいな争いをしています。
npm対応にはさらに弊害があります。DenoユーザーがDeno用のサードパーティライブラリを開発するモチベーションが下がったことです。Deno用にライブラリを作ってもDenoでしか使えませんが、npmに作ればNode.jsでもDenoでもBunでも使えます。Deno用にまだ誰も作っていないライブラリを先行して作ったとしても、npmに同じことをするライブラリが既にあれば先発優位になりません。
npm対応は悪のように書いてしまいましたが、Bunへ対抗するためにも必要だったでしょうし、企業である以上、収益を上げないといけませんのでDeno Deployユーザーを増やす目的があったのだと思います。こういうのはなかなか難しいですね。あと、書いたことはあくまで私の主観です。
というわけで、前置きが長くなりましたが、Denoのサードパーティライブラリがこれから充実する期待はできません。
そして、今はnpm対応も完全ではなく使えないライブラリが多いです。使えたとしても、npmのライブラリは依存関係が多く、Node.jsと同様にライブラリのアップデートに苦労することは間違いありません。
DenoのAPIと標準ライブラリは充実していますが、それでもDenoのAPIと標準ライブラリだけでは足りない機能はどうしたらいいのか ということになります。この問題は、Denoの開発で頻繁に遭遇します。
Wasmという選択肢
この問題の解決法としては、色々とあります。
- 自分でライブラリを作る
- 他言語のライブラリをリライトする
-
Deno.Command
でコマンドを呼び出す(実際に使用したときの記事) -
Deno.dlopen
でネイティブ言語のライブラリを呼び出す - WebAssemblyのモジュールを呼び出す
このうち、今回説明するのはWebAssemblyのモジュールを呼び出すです。
Deno.Command
とDeno.dlopen
を使った方法は、サーバーを管理していて、そのサーバーのOSに依存する高度なアプリケーションと連携する場合に有効です。そこまでの機能が必要無く、自分で作りたくない場合は WebAssembly(Wasm) はかなり有力な手段になります。
しかもWebAssemblyはDenoでもブラウザでも動きますので、様々な場面で活用できます。
DenoのWasmにおすすめの言語Zig
WasmといえばRustが有名ですが、私はZigを使っています。
理由は4つほどあります。
Zigコンパイラだけでいい
C言語の場合はEmscripten[1]、Rustの場合はwasm-pack
[2] が必要になります。そしてそれに合わせたプログラミングが必要になります。
対してZigはコンパイラオプションを付けるだけでWasm向けにコンパイルできます。
Zigはそもそもプラットフォーム間の違いを極力吸収するように作られており、その中にWebAssemblyも含まれています。Zigで書かれたプログラムはWasm向けに書かれたものでなくとも、エクスポート用の関数を書いてコンパイラオプションを付けるだけでWasmにできる可能性が高いです。
標準ライブラリが充実している
Zigの標準ライブラリは多いです。CやRustは標準ライブラリが少ないですね。
Denoの標準ライブラリに無くてもZigにあるものは沢山あります。そして、そのままWasmにできるものも多いです。標準を充実させるというのはDenoに通じるところがあります。
Wasmを利用するときというのは、複雑な計算が多いです。よく利用される計算は標準ライブラリにあることが多いです。これがZigを使っている最大の理由です。逆に標準ライブラリに使いたいものが無ければCやRustでコードを探したほうがいいかもしれません。
余計なコードが生成されない
私が使い始めた1年以上前の段階で、RustやC言語はすごく有難いことにwasmファイルだけでなく、それを呼び出すJavaScriptまで生成してくれました。ところがそのJavaScriptはブラウザ専用になっており、Denoで使おうとすると移植が必要でした。このJavaScriptは何をしているのか解析するのが難しいです。
では、wasmファイルだけ使えばいいのかというと、wasmファイルもそのJavaScriptで呼び出されるように設計されているので結局JavaScriptをしっかり理解する必要がありました。
その分Zigはそんな余計なお世話をしてくれません。作ったプログラムがそのままwasmにコンパイルされ、それを呼び出すJavaScriptも自分で0から作ることができます。
RustやCでもJavaScriptを生成せずにやる方法はありそうですが、使い慣れていないためか単純に見つけきれませんでした。
自分で0から細かい制御ができるというのは意外と強みです。
中身の理解が進みますし、何より 無駄なコードが無くなります。
私はZigの標準ライブラリのArgon2をWasmにしてみたのですが、RustやCからWasmにしている他の人のファイルサイズが60~70KBなのに対して、半分以下の25KBで済みました。
学習内容が少なく済む
RustやCでWasmをやる場合、その言語を使ったことが無ければ導入障壁が高いです。特にRustは鬼畜だと思います(笑)。
始めるまでに次のようなことを一度に学ぶ必要があります。
- メモリ・ポインタ
- 言語仕様
- パッケージ管理ソフトの使い方
- Wasm用ライブラリの使い方
- JavaScriptでWebAssemblyを扱う方法
Zigだと太字のところだけの理解でWasmを始めることができます。
主観ですが、Zig言語自体の難しさはCより難しいと思いますが、Rustよりは易しく感じます。難しさは人によって変わりますので何とも言えません。日本語情報もRustより圧倒的に少ないですし。
実際に使ってみる
今回はZigを勧める理由だけで、次の記事で、実際にDenoからZigで作ったWasmを呼び出して利用してみます。
Discussion
Rustでもコンパイラオプションだけでコンパイルできます。
生成した .wasm をJSから使う場合に
wasm-pack
を使うべきという点には同意します。コメントありがとうございます!
そうだったのですね。間違った情報でした!訂正いたします。
wasm-pack
を使った解説が多くて必須と思い込んでいました。知っていたらZigに変更せずにRustを使い続けたかもしれません。