初めて Elm に触れたので初心者向けに特徴と感想を紹介する

本エントリは Elm Advent Calendar 2019 16日目の記事です。

はじめに

どうも、さぃと (@saitoeku3) です。
Elm 界隈が温かいという話を聞いて軽い気持ちでアドカレに登録したのですが、ゴリゴリの技術記事ばかりなので緊張しています。

さて、今回は普段 React や Vue.js を書いている僕が Elm に触れて感想を紹介する記事です。そのため Elm に慣れている方よりは「これから Elm に挑戦してみたいからザックリと知りたい」という初心者向けになります。

きっかけ

僕が Elm を触ろうと思ったきっかけは周囲に Elm 好きが多かったからです。

今年度に入ってからなのですが、たまたまイベントやインターン等で知り合った人の中に Elm 好きがいたり、夏のインターン先が Fringe81 (プロダクションで Elm を採用している) だったりして普段から「Elm はいいぞ」という話をよく聞いていました。

今まで触れたことのない技術に触れるのは多少ハードルを感じるものなのですが、Elm に関してはすぐ聞ける人がいるので心配はありませんでした。仮に自分に合わなかったとしても、今回の経験をもとに Elm の良さを少しでも吸収できれば嬉しいです。

触れてみた

公式チュートリアルの日本訳 を読みながら手を動かしていました。

もともと Elm は 関数型であることJavaScriptコンパイルする言語であること公式キャラクター?がヤギであること しか知らなかったのでかなり勉強になりました。ここからは初心者が Elm を触って驚いたことや良いと思ったことを紹介します。

オブジェクト指向じゃない

関数型なので当たり前ですが、オブジェクト指向の常識が通用しません。
例えば、リスト (JavaScript の配列に似ているもの) の操作をしたいときは List モジュールを使います。JavaScript の場合は配列に生えているメソッドを呼び出して利用していたので新鮮な気分でした。

['apple', 'melon', 'orange'].length // => 3
List.length ["apple", "melon", "orange"] -- => 3

状態管理にパターンマッチングを用いる

チュートリアルの序盤にこんなコードがありました。

type alias Model = Int

init : Model
init =
  0

type Msg = Increment | Decrement

update : Msg -> Model -> Model
update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1

view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (String.fromInt model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]

React を書いたことがある人の多くはぱっと見でも既視感を感じるのではないでしょうか。
そうです。Redux の Reducer にそっくりですよね。

Redux の用語で説明すると次のようになります。

  • init: State の初期値 (Model 型)
  • Msg: Action のタイプ
  • update: Reducer

話をもとに戻すと、Elm では想定される状態更新されるパターンを事前にすべて MsgModel に定義するという特徴があります。そして、そのパターンを関数型でよく見られるパターンマッチング構文を用いて適切に処理します。

JavaScript で状態を更新するときは更新するための関数を作ってイベントハンドラに登録するのが一般的なので、コードが膨れていくと予期せぬ状態更新が行われてバグの原因になることがあります。しかし、Elm は事前にパターンを明示するお約束になっているので、安全に状態の更新ができるというメリットを感じました。

ランタイムエラーがない

チュートリアルの冒頭で紹介されているように Elm は実行上ランタイムエラーが出ないらしいです。本当か?と疑心暗鬼になって読み進めていると、どうやらエラーもデータをして扱うことでアプリがクラッシュするのを防いでいるようでいた。この秘密は Maybe 型と Result 型にあります。

Maybe 型は名前の通り「ちょうど1つの値を持っているか」「何も持っていないか」の2パターンがありえる型です。

Result 型は失敗する可能性がある処理で「処理が成功したか」「失敗した場合はなぜ失敗したのか」を表現するのに適している型です。

両者は先ほど紹介したパターンマッチング構文と相性が良くて、次のような例が載っていました。

type Error
  = BadUrl String
  | Timeout
  | NetworkError
  | BadStatus Int
  | BadBody String

-- Ok "All happy ..." : Result Error String
-- Err Timeout        : Result Error String
-- Err NetworkError   : Result Error String

どのエラーが起きたか、そのエラーに対してどんな処理をするべきかを明示的に表現できるのは素敵ですね。

おわりに

ここまでにざっと Elm の特徴の一部に触れた感想を紹介しました。
以前 Rust を触ることがあって関数型の魅力はほんの少し知ったつもりでいたのですが、改めて魅力を感じることができてよかったです。フロントエンドでもこの魅力を強みとして発揮できる Elm は面白いですね。(割と気に入ってる)

本来は Web アプリをサクッと作ったほうが使用感の比較までできてよかったのですが、しばらく忙しいので年末にチャレンジしてみます。
明日は @ababup1192 さんがモバイル UI と Elm について書かれるのでお楽しみに!!