6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 3

Elixir 1.18 の型推論を試してみる

Last updated at Posted at 2024-12-12

はじめに

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 で型推論によるコンパイル時の警告が確認できました

これで実行時エラーが減らせますね

6
0
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?