Open6

Gleam メモ

Gleam

Erlang VM 上で動く Rust で書かれた言語。Eralng VM 向けの Beam を吐く、最近は JavaScript も吐き出せるようになったらしい。

インストール

macOS であれば homebrew で簡単にインストールできる。

brew install gleam

実験的なライブラリはここ

ツアー

Language tour - The Gleam Book

playground

非公式?

Gleam Playground

翻訳

全て DeepL Pro を利用して翻訳して、少し手直ししたりしています。

JavaScript

v0.16 - Gleam compiles to JavaScript! – Gleam

以下翻訳。

なぜJavaScriptなのか?

Erlang仮想マシンはサーバ上で長時間動作するサービスには欠かせませんが、それ以外の場所では必ずしも仕事に最適なツールではありません。すべてのプラットフォームでサポートされているわけではありませんし、コンパイルされたアプリケーションのサイズ、ブートタイム、インストールのしやすさなどが問題になることもあります。

JavaScriptは、世界で最も広く使われ、広くサポートされている言語の一つです。Webサイトのフロントエンド、デスクトップアプリケーション、コマンドラインアプリケーション、携帯電話アプリ、IoTデバイス上、クラウドのサーバーレスプラットフォームなどに利用することができます。

JavaScriptは、Erlang仮想マシンのようなマルチコアや分散コンピューティングの機能はありませんが、NodeJSやウェブブラウザChromeで使用されているGoogleのV8エンジンのような高度に最適化されたランタイムのおかげで、堅牢な同時実行機能と優れたシングルスレッドパフォーマンスを備えています。

Erlangと同様にJavaScriptにコンパイルすることで、Gleamはより広い範囲の問題空間やドメインに使用することができ、より多くの人がアクセスできるようになります。バックエンドのWeb APIをGleamで書いているチームは、WebサイトのフロントエンドもGleamで書くことを選択することができ、両プラットフォーム間でコードを共有し、アプリケーションスタック全体でGleamのフレンドリーで生産的なタイプベースのプログラミングスタイルを楽しむことができます。

個人的には、Gleamのコードをサーバーレス機能プラットフォームで実行したり、ブラウザで実行される楽しいProcessingスタイルのスケッチを作ったりするのが楽しみです。

Gleamはどのように動作するのでしょうか?

Erlangコンパイラーバックエンドと同様に、この新しいJavaScriptバックエンドは、人間が読める、きれいに印刷されたソースコードを出力します。このバックエンドは現在、コンパイラに含まれており、使用するために追加のコンポーネントをインストールする必要はありません。

Gleamは、Erlangのアクターモデルのサブセットを再現しようとするのではなく、JavaScriptをターゲットにする際に、標準的なプロミスベースの並行性モデルを使用します。これは残念なことかもしれませんが、ランタイムコードの追加がないことを意味しています。これによりバンドルサイズが小さくなり、Gleamで書かれたコードをJavaScriptやTypeScriptなどの言語から普通に呼び出すことができるようになります。

今後の展開は?

完全な言語サポート

JavaScriptバックエンドは、Gleam言語のほぼすべてをサポートしていますが、特にビット文字列構文など、まだ実装されていない機能がいくつかあります。残りの機能のサポートは、次のリリースで追加される予定です。

ツール

Erlangにコンパイルするときは、Erlangのrebar3ビルドツールやElixirのmixビルドツールを使います。JavaScriptではまだビルドツールの統合はなく、代わりに低レベルのgleam compile-package --target=javascriptコマンドラインAPIを使う必要があります。このAPIは、npmのプリプロセススクリプトやmakefile、またはParcelやWebpackなどのツールとのより高度な統合によって呼び出されます。

現在、Gleamのビルドツールが開発されています。このツールは、ErlangとJavaScriptの使用に適しており、Hexパッケージマネージャからの依存関係の解決とコンパイルを処理します。

同時性の探求

JavaScriptのコードでは、Promise.prototype.thenやawaitキーワードを使って手動で降伏点を挿入する必要がありますが、ErlangやGoのような言語では、この処理はコンパイラによって自動的に行われます。

JavaScriptではPromiseタイプが普及しており、同期関数と非同期関数が分かれているため、ErlangやGoのようにそのような区別がない言語に比べて、学習や使用が難しくなっています。今のところ確固たる計画はありませんが、Gleamコンパイラの静的解析を使って、生成されたJavaScriptコードにawait文を自動的に挿入することを検討したいと思っています。

もしこれが成功して便利なものになれば、次のようなプロミスベースのGleamコードを交換することができるでしょう。

pub fn main() -> Promise(Int) {
  async_function()
  |> promise.then(fn(x) {
    let y = sync_function()
    async_function()
    |> promise.then(fn(z) {
      promise.resolve(x + y + z)
    })
  })
}

自動的に以下のようなGleamコードが生成されます。

pub fn main() -> Int {
  let x = async_function()
  let y = sync_function()
  let z = async_function()
  x + y + z
}

これらの例はどちらも同じ実行時の挙動とパフォーマンス特性を持つことになります。

これにより、JavaScriptの約束型が自動的にPromise(Promise(value))をPromise(value)にフラット化してしまい、Gleamの型システムに特殊なケースを追加しないと健全な型付けができなくなってしまうという問題も解決されます。

どのようにして試すことができますか?

最新版のGleamのインストール方法は、ウェブサイトのスタートアップページに記載されています。

このGleam JavaScriptテンプレートをインストールすると、JavaScript上でGleamコードを書いて実行することができます。

このリリースの詳細については、changelogファイルをチェックしてください。

  • Gleamの変更履歴
  • Gleam stdlibの変更履歴

以下翻訳。

よくある質問

なぜコンパイラはRustで書かれているのですか?

Gleam コンパイラのプロトタイプは Erlang で書かれていましたが、静的な型がないためにリファクタリングに時間がかかり、エラーが発生しやすかったため、Rust に変更されました。プロトタイプをRustで完全に書き直したことで、多くの技術的負債やバグが取り除かれ、パフォーマンスも向上しました。

いつかGleamにもGleamで書かれたコンパイラが登場するかもしれませんが、今はライブラリやツール、ドキュメントなど、言語の他の分野の開発に注力しています。

Gleamには型クラスがあるのですか?

その場しのぎのポリモーフィズムは、言語の人間工学に良い影響を与えるかもしれませんが、それがどのような形になるかは不明です。型クラスは1つの選択肢であり、OCamlスタイルの暗黙的なモジュールは別の選択肢であり、あるいは全く別のものになるかもしれません。

Gleamにメタプログラミングはありますか?

今のところ、Gleamでメタプログラミングがどのようになるのか、固定のアイデアはありませんが、私たちが興味を持っている分野です。もし何かアイデアがあれば、ぜひ共有してください。

Gleamにはミュータブルな状態がありますか?

Gleamのすべてのデータ構造は不変であり、構造共有を使用して実装されているため、効率的に更新することができます。

もしアプリケーションが可変型の状態を保持する必要がある場合は、アクター(再帰を使って可変型の状態を不変的にラップします)に保持させるか、Erlangのメモリ内キー・バリュー・データベースであるETSを使うことができます。

Gleamには副作用がありますか?

はい、GleamはOCamlやErlangのような不純な関数型言語なので、ファイルへの読み込みやコンソールへの印刷といった不純な動作が特別な処理なしに可能です。

将来的には、Gleamアプリケーション内の不純なコードを識別・追跡するための効果システムを導入する予定ですが、これはまだ研究の域を出ていません。

メッセージ・パッシングの型付けはどうなっていますか?

型安全なメッセージパッシングは、Gleamではコア言語の一部ではなく、ライブラリのセットとして実装されています。これにより、ErlangのOTPフレームワークを利用した安全な並行プログラムを書くことができますが、メッセージパッシングの型付けに関する特定のアプローチにロックインされることはありません。タイピングメッセージパッシングは現在進行形で研究されている分野なので、このロックインのなさは重要です。

もっと詳しく知りたい方は、GleamのOTPライブラリをご覧ください。

OTPのホットコードリロード機能は使えますか?

通常のErlangコードリロード機能はすべて動作しますが、すでに実行されているコードのタイプを知る方法がないので、アップグレード自体をタイプチェックすることはできません。これはつまり、通常のErlangの安全性はGleamの安全性よりも高いということです。

一般的に、GleamのOTPライブラリはアップグレードではなく、型安全性のために最適化されています。また、アトムモジュールではなくレコードを使用しているので、状態アップグレードのコールバックを書くのは少し複雑になるかもしれません。

GleamはAlpacaと比べてどうですか?

AlpacaはErlang VMのための静的型付け言語で、ML系言語にインスパイアされているという点でGleamと似ています。素晴らしいプロジェクトであり、大成功することを願っています。

以下に、包括的ではありませんが、違いを挙げてみます。

  • Alpacaの関数は自動化されていますが、Gleamの関数はそうではありません。
  • Alpacaの関数はオートカレーされますが、Gleamの関数はそうではありません。アルパカのユニオンはタグを付けられませんが、Gleamではカスタムタイプのすべてのバリアントには名前が必要です。
  • AlpacaのコンパイラはErlangで書かれていますが、GleamのコンパイラはRustで書かれています。
  • Alpacaの構文はML系の言語に近く、Gleamの構文はCやECMAScript系の言語に近くなっています。
  • AlpacaはCore Erlangにコンパイルされ、Gleamは通常のErlangにコンパイルされます。
  • Alpacaは素晴らしいので、ぜひチェックしてみてください :)

Gleamをプロダクションに入れるべきでしょうか?

Gleamはバージョン1.0に達していない若い言語です。堅牢ではありますが、将来的には破壊的な変更を受ける可能性があり、どこかに厄介なバグがあるかもしれません。Gleamのエコシステムも非常に若いので、他の言語にある多くのライブラリを書かなければならなかったり、純粋なGleamバージョンの代わりにErlang/Elixirのライブラリを使わなければなりません。

Erlang VMは非常に成熟していて、よくテストされているので、言語のランタイムの面では生産の準備ができています。

もしGleamから離れることに決めたら、コードをErlangにコンパイルして、将来的にそれを維持することができます。

なぜGleamという名前なのですか?

GleamはErlang仮想マシンの名前である "beam "と韻を踏んでいて、その同義語でもあります。

また、短くてかわいい言葉なので、多くの人にとってスペルや発音が簡単であることが期待されています。

いいですか?

はい、そう思います :)

Assert

技術的には失敗する可能性のある関数がありますが、実際にはそれが起こることを期待していない場合があります。例えば、プログラムはファイルを開くことから始まりますが、もしそのファイルが常に開くことが可能であることがわかっているのであれば、決して起こらないはずのエラーを処理することでプログラムを複雑にしたくはありません。

また、発生する可能性のあるエラーがあっても、プログラム内で現実的に処理する方法がない場合もあります。例えば、HTTPリクエストを処理する際にデータベースと通信するWebアプリケーションがあり、そのデータベースが応答しなくなった場合、回復できない致命的なエラーが発生したことになります。コードの中でエラーを検出することはできますが、そのあとはどうすればいいのでしょうか?リクエストを処理するためには、データベースが必要です。

最後に、エラーが発生する可能性があると思うかもしれませんが、私たちは簡単なスクリプトやプロトタイプアプリケーションを書いているので、今のところ成功の道にのみ時間を費やしたいと思っています。

このような状況のために、Gleam は assert というキーワードを提供しています。これは、パターンがマッチしない場合にプログラムをクラッシュさせるキーワードです。

assert Ok(i) = parse_int("123")
i // => 123

ここでは assert キーワードを使って、「この関数は Ok という値を返さなければならない」と言っているので、エラー処理を書く必要はありません。内部の値は変数 i に代入され、プログラムは続行されます。

assert Ok(i) = parse_int("not an int")

この場合、parse_int関数はエラーを返します。つまり、Ok(i)パターンはマッチしないので、プログラムはクラッシュしてしまいます。

クラッシュを乗り切る

フォールトトレラントであること、そしてクラッシュを乗り越えることは、Erlangのエラーハンドリング戦略の重要な部分です。Erlangのフォールトトレランスについてもっと知りたい場合は、 Gleam OTP projectLearn You Some Erlang chapter on supervisors を参照してください。

ログインするとコメントできます