19時間前にリリースされたElixir 1.18に搭載の「型推論」を試してみたが … 上手く動かせていない

この記事は、[Elixir Advent Calendar 2024 シリーズ8](https://qiita.com/advent-calendar/2024/elixir) の11日目です

昨日は、@RyoWakabayashi さんで [「Advent of code 2024 Day 8 Part 1 & Part 2 を Livebook で楽しむ
」](https://qiita.com/RyoWakabayashi/items/bcb5bba71f3a06e79c52) でした

---

[piacere](https://twitter.com/piacere_ex) です、ご覧いただいてありがとございます :bow:

19時間前にリリースされたElixir 1.18に搭載の「型推論」を試してみます

なお、このコラムが、面白かったり、役に立ったら、![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/907a8695-4d83-1b5c-8052-d0ce52de5f66.png) をお願いします :bow:

# Elixir 1.18のビルドとインストール

Elixir 1.18は、2024/12/11段階で、asdfやapt、Precompiledがリリースされていないため、自前でビルドが必要です

```
git clone https://github.com/elixir-lang/elixir.git
cd elixir
make
```

おや？ … 上記GitHub公式の手順だと、Elixir 1.19.0-devがインストールされるようですね … ま、いっか :laughing: 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/8ab6d8b5-3219-c1a9-1190-481d6c8144ca.png)

# 「型推論」を試してみる

[CHANGELOG](https://github.com/elixir-lang/elixir/blob/v1.18/CHANGELOG.md) のトップにある、関数呼出と戻り値、パターンマッチにおける型チェックエラーを試してみます

```diff_elixir
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

User.drive({:ok, %User{}}, car_choices)
```

CHANGELOGによると、下記のような型チェックが走るそうです

Elixir 1.18からは、こうした型推論に基づく型チェックが、型定義をコードに一切書いていないのに可能となるってのは、ワクワクしますね

```diff_elixir
    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}}, empty_list()

    but expected one of:

        dynamic(%User{age: term(), car_choice: term()}), dynamic()

    where "car_choices" was given the type:

        # type: empty_list()
        # from: lib/foo.ex:21:17
        car_choices = []

    typing violation found at:
    │
 22 │     User.drive({:ok, %User{}}, car_choices)
    │          ~
    │
    └─ lib/foo.ex:22:10: Example.run/0
```

しかし、サンプル通りだと、iexでは下記エラーが出ます

```diff_elixir
** (FunctionClauseError) no function clause matching in User.drive/2    
    
    The following arguments were given to User.drive/2:
    
        # 1
        {:ok, %User{age: nil, car_choice: nil}}
    
        # 2
        []
    
    iex:7: User.drive/2
    iex:17: (file)
```

そもそも、関数定義と異なる呼出サンプルのようなので、下記呼び出しに直してみます

```diff_elixir
User.drive({:ok, %User{}}, car_choices)
```

そうすると今度は、やはり異なる下記エラーが出ます

```diff_elixir
    error: undefined variable "car_choices"
    │
  2 │ set -e
    │ ^^^^^^
    │
    └─ iex:2

** (CompileError) cannot compile code (errors have been logged)
```

まぁ、そりゃ `car_choices` が未定義なので、そうなりますよね

上記型チェックエラー時に `given types:` で `empty_list()` と出ているのをヒントに、`car_choices` を試しに下記のように定義してみます

```diff_elixir
car_choices = []
```

この場合は、下記のように普通に通ってしまいました … うーん、どうやって型チェックを起こしたら良いんだろう？

```diff_elixir
{:error, :no_choice}
```

また分かったら、続報コラムを書くとします

# p.s.このコラムが、面白かったり、役に立ったら…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/907a8695-4d83-1b5c-8052-d0ce52de5f66.png) にて、どうぞ応援よろしくお願いします :bow:

---

明日は、 私で [「【再戦】19時間前にリリースされたElixir 1.18に搭載の「型推論」が上手くいった（御大José Valimとのやり取りも）」 ](https://qiita.com/piacerex/items/61ccfe455c365efebfeb) です