Elm の型注釈をできるだけ手軽に試す (Elm 0.19)

Elm の文法を勉強をする人の多くは Elm REPL (以下 REPL) を使っている場合が多いと思います。
ただし、**型注釈** については REPL 上で記述しても `I cannot handle type annotations.` というメッセージが出力されて無視されます。

REPL に頼らずコードを書いて都度コンパイルして確認したり、Elm Reactor を使ったりするのも 1つの手です。
ただ、HTML や Elm アーキテクチャを気にする必要があるので、型注釈に集中したい場合は少し不向きでしょう。

この記事ではファイルに型注釈 (と関数定義) を記述し、それを REPL からインポートする方法を紹介します [^1] 。
「もっと楽な方法があるよ」という場合は、コメントでのフォローや紹介記事の作成をしていただけると嬉しいです。

[^1]: この一文でほぼ完結しているのですが、それくらい簡単なせいかまとまっている記事が見受けられませんでした。なので、こうしてまとめることにしました。


## 想定している読者

- Elm の文法を勉強中で、Elm アーキテクチャにあまり触れていない人。
- Elmガイドの [型を読む](https://guide.elm-lang.jp/types/reading_types.html) のコードを写経していたら「型注釈」のところで「REPL じゃ動かんやんけ！」となった人。
- [基礎からわかるElm](http://www.c-r.com/book/detail/1299) P.58 を読んで「具体的に何すればええんや」となった人。


## おおざっぱな手順

1. `elm init` する。
2. `src` ディレクトリ内に Elm ファイルを作成する。
3. そのファイルで定義したモジュールを REPL 上でインポートする。

なお、エディタと REPL の切り替えコストは諦めます。


## こまかい手順

### `elm init` の実行

テキトーなディレクトリを作成し、その中で `elm init` します。
`Knowing all that, would you like me to create an elm.json file now? [Y/n]` と訊かれたらそのままエンターします。

```bash
$ mkdir foo
$ cd foo
$ elm init
Hello! Elm projects always start with an elm.json file. I can create them!

Now you may be wondering, what will be in this file? How do I add Elm files to
my project? How do I see it in the browser? How will my code grow? Do I need
more directories? What about tests? Etc.

Check out <https://elm-lang.org/0.19.0/init> for all the answers!

Knowing all that, would you like me to create an elm.json file now? [Y/n]:
Okay, I created it. Now read that link!
```

### ファイルの作成

`src` ディレクトリが作成されているので、その中で次の 3つを記述したファイルを作成します。

- `module` 文
- 型注釈
- 関数

ここでは次のような `Hoge.elm` を作成したとします。

```elm:Hoge.elm
module Hoge exposing (..)

toString : Int -> String
toString n = String.fromInt n
```


### `elm repl` の実行

`src` ディレクトリかその親ディレクトリで `elm repl` を実行します。

```bash
$ elm repl
---- Elm 0.19.0 ----------------------------------------------------------------
Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
--------------------------------------------------------------------------------
>
```


### モジュールのインポート

REPL 上で `Hoge` モジュールをインポートします。
ここでは `import Hoge` としていますが、関数呼び出しを簡便にするため `import Hoge exposing (..)` とするのもいいでしょう。お好みで。

```
> import Hoge
>
```

正しくインポートされていれば `toString` 関数を実行できるはずなので試してみましょう。

```
> Hoge.toString 10
"10" : String
>
```

できました :relaxed:

ちなみにインポートに成功した後でファイルを変更した場合、その変更は自動で反映されます。
再度のインポートは不要です。

ここまで来たら後は ファイルに型注釈と関数を記述する → REPL 上で関数を呼び出す を繰り返していけば OK です。


### おまけ：型注釈と関数定義が一致しない場合

これまでの話は型注釈と関数定義が一致していたので、特に問題は起きませんでした。
そのため「型注釈って結局何が嬉しいの？」という感想を抱いてしまうかもしれません。
記事の主旨からは逸れますが、型注釈と関数定義が一致しないコードではどうなるのか試してみましょう。

先の `toString` 関数の返す値を `String` 型のものから `Int` 型のものに変更します。

```elm:Hoge.elm
module Hoge exposing (..)

toString : Int -> String
toString n = n  -- 型注釈に反して Int 型を返す
```

この状態で REPL 上で `toString` 関数を実行します。すると…

```
> Hoge.toString 10
-- TYPE MISMATCH -------------------------------------------------- src\Hoge.elm

Something is off with the body of the `toString` definition:

4| toString n = n
                ^
This `n` value is a:

    Int

But the type annotation on `toString` says it should be:

    String

Hint: Want to convert an Int into a String? Use the String.fromInt function!
>
```

怒られます :rolling_eyes:

「`toString` 関数の型注釈 (`type annotation`) によるとこの関数は `Int` 型ではなく `String` 型ですよ」という内容のメッセージです。
型注釈の内容が反映されていますね [^2] 。

[^2]: ファイル読み込みなら型注釈を認識してくれるという… :thinking:

このように、型注釈と関数定義が一致しない場合はそのことを検出して知らせてくれます。

なお、コンパイラは飽くまで「型注釈が正しい前提」で処理をします。
型注釈が誤っている場合でも関数に誤りがあるというメッセージになるので、その点は注意しておきましょう。

**おしまり**