Flixをインストールしてテストを実行する
はじめに
最近所属する会社内でFlixというJVM系の言語が新しく出たみたいなのを見かけて面白そうだったので少し触ってみたいと思います。 色々深堀りをできそうなポイントはあるのですが一旦深く突っ込まず個々ではインストールして動かすことと、簡単に概要を把握することに努めます。
Flixとは
オーフス大学とウォータールー大学が主体となって開発されている言語で、様々な言語からインスパイヤーされています。
公式サイトには以下のように記述されています。
Flix is inspired by OCaml and Haskell with ideas from Rust and Scala. Flix looks like Scala, but its type system is based on Hindley-Milner. Two unique features of Flix are its polymorphic effect system and its support for first-class Datalog constraints. Flix compiles to efficient JVM bytecode, runs on the Java Virtual Machine, and supports full tail call elimination.
FlixはOCamlやHaskellの影響を受けており、RustやSlacaのアイディアなども取り入れているみたいですね。見た目はScalaですが、型システムはHindley-Milner
型らしいです。
Hindley-Milnerは型推論のアルゴリズムの一種であるようですが、ここではあまり主題ではないため深堀はしません。
その他にも公式サイトのWhy
によればGoのチャンネル通信ベースの非同期やElmのようなextensible records、あとはユニークな機能としてpolymorphic effect system、purity polymorphic functions、first-class Datalog constraints(こいつらについても別のブログでまとめようかとは思いますが、ここでは深くふれません)というものがあるらしいです。
JVM系の言語なのでJavaのバイトコードにコンパイルされJVM上で動きます。
動かしてみる
動作環境
動作環境は以下とおりです。
$ uname -srvmpio Linux 5.4.0-91-generic #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a LSB Version: core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch Distributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal $ java --version openjdk 17 2021-09-14 OpenJDK Runtime Environment (build 17+35-2724) OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)
Flixのバージョンなどはインストール時に明記します。
インストール
とりあえずFlixを気軽に動かしてみるには、プレイグラウンドを使って見る方法が一番簡単にできると思います。
また、VS Codoのプラグインも用意されているようでそちらをインストールするのが現段階ではコーディングをゴリゴリやっていく上では良い方法かと思います。
ただ、このブログでは、Flixのjarをダウンロードして実際に手動でテストの実行やコンパイルを行います。
flix.jarはGitHubのリリースページのところから入手できます。
今回は現状の最新である0.25.0をダウンロードしてきて、適当なディレクトリに配置します。
自分の場合は/opt/flix
ディレクトリを作成しそこにおきました。
ビルド&パッケージマネージャー
公式のBuild and Package Managementによると現状(2022/01/02)ではまだ、依存解決などの方法は提供されていないようですが、ビルドしてパッケージをシェアすること自体は可能なようです。
セントラルなパッケージのリポジトリなどはまだなくバージョンの管理や配布などはマニュアルで行わないと行けないようです。
バージョン管理などの方法については将来的には提供される予定のようです(foo-1.2.1.fpkgという名前のアーティファクト?に含まれる予定のようです)。
現状Flixは、/path/to/flix.jar <command>
のような実行方法でコンパイルやテストの実行
ここで、コマンドには以下のようなものがあります。
コマンド | 説明 |
---|---|
init | カレントディレクトリに新しいプロジェクトの作成 |
check | カレントプロジェクトにエラーがないかチェック |
build | カレントプロジェクトのコンパイル |
build-jar | カレントプロジェクトのjarファイルの作成 |
build-pkg | カレントプロジェクトのfpkg-fileの作成 |
run | カレントプロジェクトのメイン関数の実行 |
test | カレントプロジェクトのテストの実行 |
このブログでは一通りのコマンドは試そうと思います。
標準APIの一覧
Hello World
インストールと簡単な概要をまとめたところで、先ずはHello Worldを記述して実行してみます。
先ずは、プロジェクトを作成します。
$ mkdir hello-world && cd hello-world $ java -jar /opt/flix/flix.jar init $ tree . ├── HISTORY.md ├── LICENSE.md ├── README.md ├── build ├── lib ├── src │ └── Main.flix └── test └── TestMain.flix
必要なものができたみたいですね。
見た感じ、src/
にflixのコードを追加していって、test/
にテストコードを記述するみたいです。
すでにMain.flix
と言う名前のファイルが作成されHello Worldのプログラムが作成されています。
// The main entry point. def main(_args: Array[String]): Int32 & Impure = Console.printLine("Hello World!"); 0 // exit code
このPJを実行するとHello World
の文字列が標準出力に出力されます。
$ java -jar /opt/flix/flix.jar run Hello World! Main exited with status code 0.
テストの記述
プロジェクトを初期化するとすでに以下のようなテストがtest/TestMain.flix
に記述されています。
テストに関してはprinciplesのBuilt-in unit testsの項目で軽くふれられています。
Built-in unit tests
Flix supports unit tests as part of the language. We believe such integration avoids fragmentation of the ecosystem and ultimately leads to better tool support.
ライブラリーなどを使うのでは無くビルトインでテストの方法がサポートされているようですね。
@test def test01(): Bool = 1 + 1 == 2
1+1の実行結果をアサーションしているテストですね。
@test
を付けてBool型を返す関数を記述すれば良いようです。
テストに関して深く説明されたドキュメンテーションが見つからなかったのですが、おそらくMockなどの方法は現時点ではまだサポートされていないのだと思います。
上記のテストを実行すると以下のような結果が得られます。
$ java -jar /opt/flix/flix.jar test -- Tests -------------------------------------------------- root ✓ test01 Tests Passed! (Passed: 1 / 1)
テストを以下のように書き換えて失敗するようにしてみます。
@test def test01(): Bool = false
実行すると以下のような結果を得られます。
$ java -jar /opt/flix/flix.jar test -- Tests -------------------------------------------------- root ✗ test01: Returned false. (test/TestMain.flix:2:5) Tests Failed! (Passed: 0 / 1)
当たり前ですがテストは失敗しました。
それでは、独自の関数を1つ書いて、それをテストするコードを書いてみます。
関数は、人物名の文字列を受け取って、その文字列を返すだけの簡単なものを想定して作成します。
先ずはテストをTestMain.flix
以下のように追記します。
@test def testGreeting(): Bool = greeting("moheji") == "Hello!! moheji!!"
そして、空の実装の方もMain.flix
に作っておきます。
// The main entry point. def main(_args: Array[String]): Int32 & Impure = Console.printLine(greeting("moheji")); 0 // exit code def greeting(name: String): String = ???
この状態でテストを実行するとエラーになります。
$ java -jar /opt/flix/flix.jar test -- Tests -------------------------------------------------- root ✓ test01 ✗ testGreeting: Hole '?h26182' at src/Main.flix:6:38 (test/TestMain.flix:5:5) Tests Failed! (Passed: 1 / 2)
それでは実装の方を修正します。
// The main entry point. def main(_args: Array[String]): Int32 & Impure = Console.printLine(greeting("moheji")); 0 // exit code def greeting(name: String): String = "Hello!!" + " " + name + "!!"
この状態で、テストを実行すると今度は成功します。
$ java -jar /opt/flix/flix.jar test -- Tests -------------------------------------------------- root ✓ test01 ✓ testGreeting Tests Passed! (Passed: 2 / 2)
jarを作成する
flixコマンドを使えばプロジェクトをJarに固めることができます。
実行は以下の用にbuild-jar
コマンドで行います。
$ java -jar /opt/flix/flix.jar build-jar $ tree -L 1 . ├── HISTORY.md ├── LICENSE.md ├── README.md ├── build ├── hello-world.jar ├── lib ├── src ├── target └── test
コマンドを実行すると特にログ出力も無くプロジェクトルートにhello-world.jar
が作成されているのがわかります。
このjarを実行してみます。
$ java -jar hello-world.jar Hello!! moheji!!
正しく実行を行えているようですね。