次世代の軽量コンパイラ言語「Ante」を触ってみる
Anteという言語を見かけ、ホームページのアピールポイントを見るになかなか良さげなので触ってみることにしました。
Anteは「低レベル関数型言語(a low level functional language)」を名乗っています。この言葉を聞くとまずRustが思い浮かぶわけですが、実際この言語も今のところRustで実装されています。
Rustは文法がヘビーなところ以外はほとんど文句なしというのが専らの評判だと思います。Anteはこのヘビーなところを易しくしましたというコンセプトなのでしょう。
良さげな文法機能
篩型
篩型は、既存の型に述語関数(Bool型を返す関数)で制約をかけるものです。
型レベルのガード節みたいなものです。
dotproduct (v1: Array u64) (v2: Array u64 where len v1 == len v2) -> u64 =
// Sum the products of each element of both arrays
products = map2 v1 v2 (*)
sum products
dotproduct [1, 2, 3] [4, 5, 6] //=> 32
dotproduct [1, 2, 3] [4, 5] //error, failed to prove len [1, 2, 3] == len [4, 5]
where
以下でガードを掛けています。このコードは、配列の長さが同じときのみ配列をベクトルとみなして内積を定義する、ということがしたいのでしょう。
これが実行時ではなくコンパイル時にチェックできるのです。
ライフタイム推論
Rustの鬼門の一つはライフタイムの習得だと思います。Anteはこれに対して、いやこんなものはコンパイラが推論するから不要、という態度をとるようです。
確かにライフタイム付きの参照を参照の派生とみなせば、これは型推論の問題となりそうです。ライフタイム指定が不要になればかなり助かりますが、果たしてそんなにうまくいくのでしょうか。ホームページでもこのあたりの仕様はまだ確定していないようです。
type Person = job: string, name: ref string
// The data referenced via `&` should not be freed inside this function
make_person job =
Person job &"bob"
// bob is only used at this scope, so it can be safely freed afterward.
bob = make_person "programmer"
// unlike ownership systems, aliasing is allowed in region inference
bob_twin = bob
assert (bob.name == bob_twin.name)
代数的効果
AnteはAlgebraic effects(代数的効果)も言語レベルでサポートするようです。詳しくは理解していないのですが、エラーハンドリングや副作用の明示などに使えて、モナドより使い勝手が良いらしいです。
// computing expected value from a function that takes a coin flip effect.
effect Flip with
flip: unit -> bool
calculation () =
if flip () then
unused = flip ()
if flip () then 0.5
else 4.0
else 1.0
expected_value (f: unit -> f64 can Flip): f64 =
handle f ()
| flip () -> (resume true + resume false) / 2.0
print (expected_value calculation) //=> 1.625
しかし、この機能を使ってみようとしたところ、コンパイラがエラーを起こしました。どうやらこの機能はまだ未実装のようです。
導入
バイナリ等は配布されていないと思われますので、READMEに従ってcargoでビルドします。
git clone https://github.com/jfecher/ante.git
cd ante
cargo install --path . --no-default-features
Anteはデフォルトでコード生成にLLVMを使っているようですが、--no-default-features
をつけると独自のバックエンドでコード生成するようです。どれほどパフォーマンスが変わるのかはよくわからないのですが、LLVMの導入は結構めんどくさいので--no-default-features
をつけることをおすすめします。後述するようにこのあたりで躓きました。
つまづいたとこ
AnteはLLVMの導入にあたって、llvmenv
というツールを使用することを推奨しています。これ自体はcargo install llvmenv
でインストールできます。で、READMEに従ってビルドしようとしたのですが、
$ cargo install --path .
...
Compiling inkwell_internals v0.5.0 (https://github.com/TheDan64/inkwell?branch=master#25b9fc58)
error: No suitable version of LLVM was found system-wide or pointed
to by LLVM_SYS_130_PREFIX.
Consider using `llvmenv` to compile an appropriate copy of LLVM, and
refer to the llvm-sys documentation for more information.
llvm-sys: https://crates.io/crates/llvm-sys
llvmenv: https://crates.io/crates/llvmenv
というエラーに出くわしました。LLVM_SYS_130_PREFIX
はちゃんとセットされているのですが。
結論としては、このエラーの原因は僕がシェルにzshを使っていたためで、
source <(llvmenv zsh)
と打つと解決しました。
所感
コンセプトとしては悪くないと思います。Rustの後釜としては有望なのではないでしょうか。
ただ現時点では未完成の機能が多く、動作も不安定です。実用性はまだまだとみて良いでしょう。
Discussion