趣味から始めて、仕事でもElmを使うようになった話

こんにちは。[Elm2(完全版) Advent Calendar 2018 - Qiita](https://qiita.com/advent-calendar/2018/elm2)の12月11日の担当の[@gaaamii](https://twitter.com/gaaamii)です。
技術的な内容ではない身の上話が多めです。ごめんなさい。

## 3行で
- Elmをなんとなく触り始めて
- 仕事でも使えるようになった
- 良いことも、難しいこともあった

## 経緯
以下、Elmをなんとなく触り始めてから、仕事で使うようになるまでの経緯を書きました。

### 趣味でMarkdownメモ帳の開発
昔々、ブラウザで動くいい感じのMarkdownメモ帳が欲しくて、作ったものがありました。

- [Backbone.jsを使ってブラウザベースのMarkdownエディタを作りました - gaaamiiのブログ](https://shgam.hatenadiary.jp/entry/2014/10/16/180123)

当時はJavaScript MVCのフレームワークといえばBackbone.jsかangular.jsかという感じでした。

ビルドツールにはgulpを、パッケージ管理にbowerを使っていたりして、時代を感じます。

- https://github.com/gaaamii/nekobito-backbone

なんとか最低限使えるものにはしたものの、2015年春にデータの保存先として使っていたDropbox Datastore APIの廃止が発表され、同時に私自身も就職して忙しくなったので、これに手を入れることはほぼなくなりました。しかしその後もなんとなく気になっていて、何かしらのいけてるライブラリで作り直すなりしたい、という気持ちだけは持っていました。

- [gaaamiiさんのツイート: “Riot.js使ったほうが話題になりそうだからRiot.js使うか https://t.co/ZDamSINQ9v”](https://twitter.com/gaaamii/status/761562906324377600)
- [gaaamiiさんのツイート: “Elmで https://t.co/XN3SkA3f5N のリニューアルにチャレンジしてるけどElmさっぱりわからない”](https://twitter.com/gaaamii/status/928716275341189120)

そんなことをふわふわ考えている中で、なぜかVueやAngularには惹かれず、Riot.jsはドキュメントちょっとは眺めたのですが、趣味開発の時間を取らないうちに興味を失ってしまいました。また、ReactやReduxがいけてるのじゃないかと思っていたものの、仕事でReactを使っているのでなにか違うものを使ったほうが面白いんじゃないかと思い、こちらも使いませんでした。

そういう感じで、大した志もなく、強いて書くなら以下のような理由でElmを触り始めました。

- Reduxの元ネタだから、Elmを仕事で使うことはなくてもReduxやるときに役に立ちそう
- Clojureを一瞬触ろうと思った時期があったけど結局勉強が続かなくて、しかし何かしら関数プログラミングっぽい言語を一つは身につけておきたいと思っていた
	- なんでそう思っていたかというのはおそらく「ハッカーになるには」というエッセイの[この辺の文章](https://cruel.org/freeware/hacker.html#skills1))の影響。

作っているものは https://nekobito.github.io にあり、その開発メモはだいたいScrapboxの[Nekobito - きまべん](https://scrapbox.io/kimaben/Nekobito)に書いてあります。手探りで学びながら、コードを書いていました。

## 仕事での導入チャンス
昨年の今頃、仕事ではReactを書いていましたが、たまたま社内の週１の勉強会でElmの発表をする機会が何度かありました。その当時から仕事で使うぞという気持ちがあったわけではなく趣味の範疇におさめておくつもりだったのですが、社内では「なんかgaaamiiがElmやっているらしいぞ」というのが知られました。

そうこうしている間に、今年の夏、仕事で新たにちょっとしたサブシステムが必要になり、その小さなSPAのためのウェブフロントエンドの言語として、Elmを採用できるんじゃないかという可能性が出てきました。

自社のプロダクトのほとんどは、サーバーサイドはRuby on Railsで作られており、ウェブフロントエンドはJavaScriptかTypeScriptです。Elmでウェブのフロントエンドを書くことはけっこう挑戦的でしたが、会社としても他の言語に手を伸ばしていきたいという話もあり、CTOとチームリーダーから許しを頂き、Elmでの実装に至りました。

## Elmについて学んだこといろいろ
Elmを書くと決めて、いろいろ学ぶ必要がありました。今でもすべて理解ができているとは言い難いので、改めて調べながら書いています。基本的には、[Introduction · An Introduction to Elm](https://guide.elm-lang.org)を上から読んでいけば良いと思います。

- TEA(The Elm Architecture)
- 関数シグネチャ（型）の読み方
- typeについて
- JSとのつなぎ方

### TEA(The Elm Architecture)
- [The Elm Architecture · An Introduction to Elm](https://guide.elm-lang.org/architecture/)

TEAについては、もともとReduxについてある程度知っていたので、それとほぼ同じものとして理解できました。Reduxではactionをdispatchするとreducerで定義された変更が反映された新しいstateが得られる。Elmでは `update : Msg -> Model`で、Msgがactionに相当します。viewはmodelの値に応じたHTMLを返す関数 (`view : Model -> Html msg`)です。

### 関数シグネチャ（型）の読み方
Elmを使うためには、関数シグネチャの読み方について知る必要がありました。Elmでは、モジュールとして整理された関数があり、その関数に決められた型の引数を渡して、出力を得るだけですが、学びはじめた当初、**すべての関数が一つの引数しか受け取らない**ということに、少し戸惑いました。以下のブログ記事に書きました。

- [Elmやってるけどどうか（途中経過） - gaaamiiのブログ](https://shgam.hatenadiary.jp/entry/2018/08/20/234857)

### typeについて
Elmでは、あらゆるものが関数です。これはシンプルでわかりやすい。関数を定義するときは  `fuga = "fuga"`とか`hoge = (\_ -> "hoge")` みたいな形で、名前をつけて`=` で束縛すればよく、その名前を打ち込めば実行できる。

ただ、typeという特別なキーワードがあります。 `type Hoge = Foo String` みたいなのを書きます。`type`ってなんなんだ、という話になります。	

`type`の使い所は、Type Aliasと、Custom Typesの二種類です。

#### Type Alias
これは特に難しくありません。自分でデータ型を定義するだけです。

```elm
type alias Hoge = { id : String }
```

すると、関数シグネチャを書くときに使えます。

```elm
outputHoge : Hoge
outputHoge hoge =
  hoge
```

普通ですね。ただ面白いことに、この型エイリアスも関数になっています。それをElmのREPL上で見ておきます。

上で定義した、Hogeだけで出力を見ます。

```elm
Hoge
<function> : String -> Hoge
```

`String -> Hoge`な関数です。ということは…？

```elm
> Hoge "hoge"
{ id = "hoge" } : Hoge
```

Stringを渡すとレコードが返ってきました。

じゃあ、レコードのフィールドがたくさんあると…？

```elm
> type alias Fuga = { id : String, name : String }
> Fuga
<function> : String -> String -> Fuga
```

`String -> String -> Fuga` です。ということは….？

```elm
> Fuga "foo" "bar"
{ id = "foo", name = "bar" } : Fuga
```

おお〜。フィールドが2つでも、順にフィールドに文字列が入ったレコードが返ってきました。

#### Custom Type
こちらはもう少し難しかったです。

> Note: Custom types used to be referred to as “union types” in Elm. Names from other communities include tagged unions and ADTs.

- [Custom Types · An Introduction to Elm](https://guide.elm-lang.org/types/custom_types.html)

他の言語では、ユニオン型とかタグとか、Algebraic data typeとか呼ばれるみたいです。なるほどよくわからない。

Elmで真っ先に目にするのは、`Msg`という型ではないでしょうか。TEAで、update関数のパターンマッチをするためのパターンが、これを使って表現されています。

単純なのはこういう形です。

```elm
type Msg = OnClick | OnInput

update : Msg -> Model -> (Cmd Msg)
update msg model =
  case msg of
    OnClick -> ({ model | clicked = True }, Cmd.none)
    OnInput -> ({ model | input = True }, Cmd.none)
```

起きうるイベントを`type`で定義しておいて、それに応じて次の状態（model）をつくっています。

しかし、実際のアプリケーションでは、なにかイベントが起きたときに、そのイベントに紐づくデータを使うことがほとんどです。`OnInput`であれば、ユーザーが入力しているその入力フォームの値です（そもそも上のまま`Html.Events.onInput OnInput`と渡そうとすると型が合わなくてエラーになる）。

そういう場合は、このようにします。

```diff
- type Msg = OnClick | OnInput
+ type Msg = OnClick | OnInput String

update : Msg -> Model -> (Cmd Msg)
update msg model =
  case msg of
    OnClick -> ({ model | clicked = True }, Cmd.none)
-    OnInput -> ({ model | input = True }, Cmd.none)
+    OnInput inputValue -> ({ model | inputValue = inputValue }, Cmd.none)
```

このとき、msgは`OnInput "text"`みたいなものになっています。OnInputというカスタム型に、Stringのデータがくっついています。わかりづらいのでREPLで示すと、こういうことになっています。

```elm
> type Msg = OnInput String
> OnInput
<function> : String -> Msg
> OnInput "text"
OnInput "text" : Msg
> case (OnInput "text") of OnInput text -> "ペイロードは" ++ text ++ "です"
"ペイロードはtextです" : String
```

こういうことをREPLで評価しながら、ふむふむそういうことかというのが理解できました。

### JSとのつなぎ方
Elmはまだライブラリが十分に充実しているとはいえず、たとえばlocalStorageにつなごうと思ったらJSでやる必要がありました。ElmをJSと共に使うには、portsという仕組みを使って、Elm側で扱う値やイベントを、JSで書いたプログラムとやりとりをするような書き方をします。

- https://guide.elm-lang.org/interop/ports.html

## Elm実際どうなのか
趣味で使い始めて、小規模とはいえ仕事でも使うようになって、実際Elmの使い勝手はどうなのか。良さと難しさについて書きます。

### 良さ

#### 関数だけ
ややこしい言語仕様はなく、全部関数なのでシンプルです。

#### No Runtime Error
おかしいところがあればコンパイルエラーになります。

#### elm-format
elm-formatというツールのおかげで、lintとかいろいろ設定しないとコード書けないみたいな面倒さがない。

#### Debugger
単純で使いやすくて好きです。その時点に戻ってChromeのDevツール開いてHTMLやらスタイルを確認したりというのをよくやります。

#### REPL
上で書いてきたように、たいていのことはREPLで確認できます。これは自分の書いている実装についてもそうです。自分のプロジェクトのディレクトリで`elm repl`を打ち込み、もし関数が`MyModule`というモジュールの`myFunc`という名前なら、`import MyModule exposing (myFunc)`と打てば、その関数をREPL上で実行できます。

### 難しさ

#### Elm 0.19にする対応
こちらに書いた通りです。

- [elm-upgradeに従ってElmのバージョンを0.18から0.19へ上げる - Studyplus Engineering Blog](https://tech.studyplus.co.jp/entry/2018/10/29/100618)

小規模アプリでもそこそこ大変でした。

#### Json.Decodeつらい問題
APIとつないだときに、そのレスポンスをElmのデータ型にするというときに `Json.Decode`というモジュールを使います。JSONはただの文字列なので、こういう処理はどの言語でも必要になると思います。こういう形になります。

```elm
import Json.Decode as Decode

userDecoder : Decode.Decoder User
userDecoder =
    Decode.map2 User
        (Decode.field "id" Decode.string)
        (Decode.field "name" Decode.string)

-- > Decode.decodeString userDecoder "{\"id\":\"hgoehoge\",\"name\":\"fugafuga\"}"
-- Ok { id = "hgoehoge", name = "fugafuga" }
```

レスポンスの型の数だけ、このデコーダーを書かないといけないのがなかなかつらいです。現状はどうにかならないものかという感じですが、実はなにか良い方法があるのかもしれない。

#### モジュール分割の方針
Elmでは、JSのようにモジュールを細かに分割することが勧められていません。Reactのように、コンポーネントとして分割するのもよろしくないようです。

とはいえ、`Main.elm`にすべての関数を入れてしまっていいのか？というと、そうではないと思います。

改めてモジュールを分ける意味を考え直す必要がありました。
この辺も、どう考えたらいいのか難しいと感じています。

#### 学習コスト
自分しかやっていない言語を仕事で導入するというのは、かなり自分勝手で悪いことをしてしまったんじゃないか…という気持ちになることもあります。学習コストばかり高いものを無責任に周りに強いてるんじゃないかと。

しかし、Elmには上に書いたような良さがあるので、新しいことを学んででも使う価値はあるはず。そう思ってやっています。

それまではElmを楽しむだけだったのですが、仕事で使っている以上はもっと理解を深めて、Elmを使いたくなるようなメリットを社内外へ伝えていかなくてはいけません。難しいですが、これも頑張りたいところです。

## まとめ
最初は、個人開発でなんか新しいことやりたい、会社の勉強会のネタにでもなればいいな、くらいの気持ちで始めたんですが、思ったより楽しく実用的なものだったので、仕事で使えるまでに至りました。

Elmを使っていて困るのは、最初の学習コストと、新しいバージョンへの対応が大変なことくらいだと思います。
ウェブアプリの実装に、関数だけのシンプルな言語を使ってみたい方にはおすすめです。ぜひ。