はじめに
Elixir 1.18 のリリース候補(RC版)が出ました
型推論ができるようになったとのことで試してみます
Elixir 1.18.0-rc.0 のインストール
asdf で 1.18.0-rc.0 をインストールします
asdf install elixir 1.18.0-rc.0
カレントディレクトリー配下で 1.18.0-rc.0 を使うように指定します
asdf local elixir 1.18.0-rc.0
プロジェクトの作成
新しいプロジェクトを作成します
mix new type_check
Elixir プロジェクトに必要な最低限のディレクトリー構造とファイルが作成されます
ディレクトリー内に移動します
cd type_check
User モジュールの定義
lib/user.ex
を作成し、 CHANGELOG に書いてあったものと同じ定義を書きます
defmodule User do
defstruct [:age, :car_choice]
def drive(%User{age: age, car_choice: car}, car_choices) when age >= 18 do
if car in car_choices do
{:ok, car}
else
{:error, :no_choice}
end
end
def drive(%User{}, _car_choices) do
{:error, :not_allowed}
end
end
メインモジュールの変更
lib/type_check.ex
を以下のように変更します
defmodule TypeCheck do
def hello do
car_choices = [:toyota]
User.drive({:ok, %User{}}, car_choices)
end
end
User.drive に渡している変数の型が不正です
コンパイルの実行
警告をエラーとしてコンパイルを実行します
mix compile --force --warnings-as-errors
実行結果
Compiling 2 files (.ex)
warning: incompatible types given to User.drive/2:
User.drive({:ok, %User{age: nil, car_choice: nil}}, car_choices)
given types:
{:ok, %User{age: nil, car_choice: nil}}, non_empty_list(:toyota)
but expected one of:
dynamic(%User{age: term(), car_choice: term()}), dynamic()
where "car_choices" was given the type:
# type: non_empty_list(:toyota)
# from: lib/type_check.ex:3:17
car_choices = [:toyota]
typing violation found at:
│
4 │ User.drive({:ok, %User{}}, car_choices)
│ ~
│
└─ lib/type_check.ex:4:10: TypeCheck.hello/0
Compilation failed due to warnings while using the --warnings-as-errors option
型推論により、不正な型が渡されていることが検出できました
1.17 でのコンパイル確認
mix.exs
を開き、 Elixir のバージョン下限を編集します
defmodule TypeCheck.MixProject do
use Mix.Project
def project do
[
app: :type_check,
version: "0.1.0",
- elixir: "~> 1.18-rc",
+ elixir: "~> 1.17",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
...
使用する Elixir のバージョンを変更します
asdf local elixir 1.17.3
この状態でコンパイルします
mix compile --force --warnings-as-errors
実行結果
Compiling 2 files (.ex)
Generated type_check app
特に問題なくコンパイルできてしまいました
戻り値の型チェック
Elixir バージョンを 1.18.0-rc.0 に戻します
asdf local elixir 1.18.0-rc.0
lib/type_check.ex
を以下のように変更します
defmodule TypeCheck do
require Logger
def hello do
user = %User{age: 18, car_choice: :toyota}
car_choices = [:toyota]
case User.drive(user, car_choices) do
{:ok, car} -> car
:error -> Logger.error("User cannot drive")
end
end
end
コンパイルしてみます
mix compile --force --warnings-as-errors
実行結果
Compiling 2 files (.ex)
warning: the following clause will never match:
:error
because it attempts to match on the result of:
User.drive(user, car_choices)
which has type:
dynamic({:ok, term()} or {:error, :no_choice} or {:error, :not_allowed})
typing violation found at:
│
9 │ :error -> Logger.error("User cannot drive")
│ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
│
└─ lib/type_check.ex:9: TypeCheck.hello/0
Compilation failed due to warnings while using the --warnings-as-errors option
戻り値の型が不正であるため、エラーになりました
このパターンも 1.17 ではコンパイルできてしまいます
まとめ
Elixir 1.18 で型推論によるコンパイル時の警告が確認できました
これで実行時エラーが減らせますね