Elm文法総復習

# はじめに
皆さん、Elm書いてますか？私も最近入門してみたところです。

Elmの文法はHaskellをベースにしているだけあって似たようなところが多く、Haskellの経験がある人にとっては書きやすい言語であると思います。ただ、Haskellの問題点を解決するためであったり、Elm特有の事情のためにHaskellと若干文法が異なっているケースがあります。

そこでこの記事ではHaskellの経験がある人に向けて、ちょっと引っかかりやすいポイントを含めた説明していきたいと思います。

Elmのreplか[オンラインエディタ](https://elm-lang.org/try)を用意して、打ち込みながら試していきましょう！

---

# 文法一覧
ではさっそくざっくりと文法の一覧を紹介していきましょう！

---

## リテラル
リテラルにはざっくり以下の3つが存在します。

- 数値
- 真偽値
- 文字

それぞれ詳しく見ていきましょう。

---

### 数値リテラル

```elm
> 37
37 : number
> 2.718
2.718 : Float
```

最終的にJavaScriptに変換されるため、Haskellと勝手が異なります。単に整数を記述すると`number`に推論され、その後利用箇所に応じて`Int`または`Float`として扱われます。小数を記述すると`Fractional`ではなくいきなり`Float`型に定まります。

---

### 真偽値リテラル

```elm
> True
True : Bool
> not True
False : Bool
```

これはHaskellと一緒ですね。大文字から始まるところも、否定に`!`ではなく`not`を使うところも同じです。

---

### 文字に関するリテラル

```elm
> 'c'
'c' : Char
> "abc"
"abc" : String
```

シングルクォートが`Char`型、ダブルクォートが`String`型なのは一緒です。蛇足ですがHaskellと違って`String`型は`[Char]`のエイリアスではないことに注意してください。

また、複数行文字列リテラルを扱うことができます。ダブルクォート3つで挟んでください。内部でダブルクォートを使用することもできてとっても便利です。ただしReplでは試しにくいです。

```elm
"""
Elm Kansai!
Let's "enjoy"!!!
"""
```

---

## 関数定義

```elm
square : Int -> Int
square n = n * n
```

これもHaskellではよく見かける定義方法とほぼ一緒ですね。型を書いてから、関数の中身を定義していきます。ただし先ほどからちょくちょく出てきていますが、型の指定にはひとつだけのコロン`:`を使います。Haskellではふたつ`::`だったのでクセが残っている人は超注意です！

また、Haskellと同様にトップレベルで定義する関数にはドキュメントを兼ねて型を書くのがベターです。また、不必要に広い範囲の型推論を避けて、想定外の利用を防止することができるメリットもあります。

---

## Lambda式

```elm
f = \n -> n * n
```

mapの引数とか割といろんなところで使いたくなるLambda式。これもHaskellと一緒で$\lambda$を模したバックスラッシュ`\`を使って定義します。

---

## リスト

```elm
> [1, 2, 3, 4, 5]
[1,2,3,4,5] : List number
> 1 :: [2, 3, 4, 5]
[1,2,3,4,5] : List number
> 1 :: 2 :: 3 :: []
[1,2,3] : List number
```

カンマ区切りで書けるのは他の言語ともだいたい一緒です。ここでもやはりHaskellと一緒で、空リスト`[]`に結合関数`::`でくっつけていく方式でリストが成り立っています。ただしHaskellではリストの先頭に要素を結合する関数が`:`だったのに対してElmでは`::`になっていることに注意が必要です。関数と逆ですので！

---

## タプル

```elm
t = (False, "Foo", Just 5)
(name, age) = ("Thomas", 37)
```

リストと違って固定長で、中身の要素は型が違っても大丈夫なのがタプルです。これもHaskellとの違いは無いので大丈夫でしょう。

---

## レコード

```elm
type alias Point =
  { lat : Float
  , lon : Float
  }

p1 = 
  { lat = 139.744858
  , lon = 35.675888
  }
```

Haskellでは`data`でレコードを作りますが、Elmでは型エイリアスで定義します。あとは代入したり、関数の型に書いたりして使うだけです。

また、レコード内の値の読み取りは`.field_name`でアクセスします。これは関数として定義されますが、JS風に後ろにくっつけることもできます。レコード内のフィールドと同名の値取り出し用関数が定義されるHaskellとは異なるところですので注意が必要ですね。例えば今回の場合は以下のようにアクセスします。関数としても使えるので、`map`の引数にも簡単に与えられます！

```elm
> .lat
<function> : { b | lat : a } -> a
> p1.lat
139.744858 : Float
> .lat p1
139.744858 : Float
> List.map .lat [p1]
[139.744858] : List Float
```

ただし注意が必要な点として`.lat`は「何でも良いけど`lat`を含むレコードの中にある`lat`の値を取ってくる」関数であって「`Point`だけを引数に取る」関数ではないというところです。`Point`とは全然関係ない`lat`というフィールドを持ったレコードに対しても使用できてしまいます。

```elm
> a2 = {lat = "FOOOOO!!"}
{ lat = "FOOOOO!!" } : { lat : String }
> .lat a2
"FOOOOO!!" : String
```

---

## if式

```elm
n = 5
s = if (n < 5) then "It's small" else "It's big!"
```

おなじみif式です。`else`が必須であること、返す値の型を全て同一にしなければならないことも含めてHaskellとの違いも特にありません。筆者は他の言語と行ったり来たりしているうちによく`then`を書き忘れてしまいがちなので、皆さんもお気を付けください。

---

## case式

```elm
maybeInt = Just 5
m = case maybeInt of
  Just n -> n
  Nothing -> 0
```

これもHaskellと一緒です。中でパターンマッチを使うこともできるのでMaybeやList、レコードを使うときなどに重宝するでしょう！

---

## let-in式

```elm
let
  a = (2 - 1) ^ 2
  b = (3 - 5) ^ 2
in
  sqrt (a + b)
```

これもHaskellでおなじみ`in`の中で使う値を`let`で計算しておける便利構文です。なおElmでは`let-in`式で十分という理由[^1]から`where`を実装していません。今後もおそらく実装されない見通しなので諦めましょう。

---

## カッコの省略

```elm
> List.map ((+) 10) (List.filter ((>) 5) (List.range 1 10))
> List.map ((+) 10) <| List.filter ((>) 5) <| List.range 1 10
[11,12,13,14]
```

ちょっと文法とは異なりますがせっかくなので紹介。カッコばかりで見づらくなるためHaskellでよく使う`($)`はElmでは`<|`と書きます。右から順番に適用していくイメージですね。

---

## パイプ

```elm
> 5 |> (\n -> n + 10) |> String.fromInt
"15" : String
```

先ほどのものと対になるこちらも紹介。Unixでおなじみのパイプを再現したような動きをするパイプ`|>`がElmにはあります。実はHaskellにも`Data.Function`に`(&)`という関数が定義されていて同じ事ができます[^2]。

個人的に、Elmではどちらかというと左から自然に読めるパイプの方が好まれている印象を受けました。

---

## コメント

```elm
-- 1 line comment
{-
Multiple
Lines
Comment
{- and nested! -}
-}
```

これは完全にHaskellと同一ですね。ネストできるところも使いどころによっては便利かもしれないです。

---

## 型クラス

```elm
> List.map (\n -> n + 1) (List.range 1 10)
[2,3,4,5,6,7,8,9,10,11]
> Maybe.map (\n -> n + 1) (Just 5)
Just 6
```

Elmにはいわゆる型クラスに相当するものが実装されていません。個別のクラスそれぞれで`map`や`filter`などが定義されています。従って先ほどからちらほら出てきているように、利用時にはプレフィックスが必要となります。

ただし例外的に`appendable`, `comparable`, `number`の型はいわゆる型クラス的な動きをします[^3]。しかし自作型をそれらのサブクラスにしたりすることはできません。また、自分でこのような働きをするクラスを定義することもできません。

```elm
> (++)
<function> : appendable -> appendable -> appendable
> [1, 2, 3] ++ [4, 5, 6]
[1,2,3,4,5,6]
    : List number
> "Foo" ++ "Bar"
"FooBar" : String
```

```elm
> (>)
<function> : comparable -> comparable -> Bool
> "foo" > "bar"
True : Bool
> 1 > 2
False : Bool
```

```elm
> (+)
<function> : number -> number -> number
> 1 + 2
3 : number
> 1.5 + 5.3
6.8 : Float
```

---

# おわりに
元々Haskell界隈からこの言語を知ったので、主にHaskellとの違いに触れつつ文法に関する事項について整理してみました。ほぼ一緒なところもありつつ、微妙に違ったりするところもあるのでHaskellから移ってきた人は若干注意が必要かもしれません。

JSや設計思想との兼ね合いでHaskellからは思い切って機能を削ってきているところもありますが、Webフロント開発をする上で十分であろうとは思います。そういうのをバリバリ使いたい人はGHCJSなどを検討すると良さそうです。

とにもかくにもReplで気軽に試しつつ、Elmの世界に浸ってもらえればと思います！

[^1]: [Why does Elm support ‘let’ but not ‘where’?](https://discourse.elm-lang.org/t/why-does-elm-support-let-but-not-where/1634/7)
[^2]: 今回ちゃんと調べて初めて知りました。[Hoogle](https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Function.html#v:-38-)に載ってます。
[^3]: このほかに`compappend`というListとStringを統一的に扱う型が定義されていますが、コンパイラが使うだけでユーザー側から扱うことはありません。