Elm Advent Calendar 2016 - Adventar 5日目
"... every Elm function takes only one argument"
Richard Feldman, Elm in Action MEAP V03
長年の疑問が一気に解決された
Elmの戻り値は一つだけ
こっちはeasy partだった
Elmが式であり、式は評価されると1つの値を返す
-
early return
できない - そもそも
return
がない
等々の「そこから導かれる話」には面白いものがあり、勉強になった。
が、基本easy part
Elmの関数型定義で->
が複数回登場するパターン
長い間うまく理解できてない(自分でうまく説明できない)ことの1つ
update : Msg -> Model -> ( Model, Cmd Msg )
viewEntries : String -> List Entry -> Html Msg
こういう関数の型定義の「読み方」がわからなかった
腑に落ちる説明に出会うこともなかった
-
->
はただのセパレータ?- それだと、引数と戻り値の境界がゆるふわすぎる
-
Msg
やModel
が引数なんだったら、Msg Model -> ( Model, Cmd Msg )
、という風に書くのが自然では?-
->
の前に引数、後ろが戻り値 - 引数複数とるなら、``Msg Model -> ...` と関数定義してるときのように並べる
- しかし、そういう風になってないのなら、何か理由があるはず
-
- ElmのSyntaxが影響うけてるHaskellとかMLとかのせいでは?
- 話が広がりすぎてるし、シンタックス1つ説明するのに別の言語を持ち出すのは、うまいやり方ではないと思う
- 影響関係については確定的でないことが多いので無駄足が増える
c.f.
update : Action -> Model -> Model
. This function takes an Action and a Model as arguments (in that order), and returns a Model. Or, "update has type Action goes to Model goes to Model."
- How to Read a Type Annotation
-
elm-for-jsという、javascriptエンジニア向けにElmを説明する記事してるリポジトリ
- Elmアーリーアダプターが書いてる
- Elmが
v0.16
時代に書かれてて、v0.17
、v0.18
に追随できてないので、半分はdeprecatedになってる
- Haskellとか関数型言語を使いこなすFunctional polyglotにによる説明よりもとっつきやすい
- ここでホストされてる記事は何度か読んだが基本的にどれも勉強になった
- とはいえ、
->
は今ひとつだった- 「セパレータです」という説明しかしてない
- 、複数
->
が登場する関数型定義を取り上げた箇所では、「->
は go toと発音する」とかなんだかよく分からない説明(Msg
がModel
へ gotoする?)
Elmの関数の引数は1つだけ
という状況を救ってくれたのが、Richardの見事な解説
「複数の引数をもつ関数の型定義」というセクション見出しから予想してた内容とは別物だった
Richardの解説のポイント
String
モジュールのpadLeft
は
> String.padLeft 9 '.' "not"
"......not" : String
という使い方をする、3つ引数を渡してる。いわゆるleftpad系のヘルパー関数
この関数に対して、Richardは関数の部分適用を使いながら、型定義を作り上げる
まずpadLeft
に9
だけを渡す
> String.padLeft 9
<function> : Char -> String -> String
エラーにならない
部分適用して返された関数をpadNine
と定義する
padNine = String.padLeft 9
このpadNine
に今後は '.'
だけ渡す
> padNine '.'
<function> : String -> String
返された関数をpadNineDots
を定義する
padNineDots = padNine '.'
ここでpadNine
とpadNineDots
の型定義だが
padNine: Char -> (なんらかの関数)
padNineDots: String -> String
- 1文字(
Char
)受け取って関数返す関数 -
String
受け取りString
返す関数
となるので、padLeftの型定義をpadNine
とpadNineDots
の型定義をもとに作ると
padLeft : Int -> (Char -> (String -> String))
関数を返す..関数を返す...関数、としてpadLeft
が型定義できる
ここで1ステップあり、Elmでは、
padLeft : Int -> (Char -> (String -> String))
からカッコ()
を外すことが言語レベルでサポートされてるので
padLeft : Int -> Char -> String -> String
という風にすっきり書くことできる。
これが、**「デフォルトでカリー化されてる」**ということから派生する話
padLeft : Int -> (Char -> (String -> String))
padLeft : Int -> Char -> String -> String
どっちも関数の型定義としては正しい。
下の方は引数が3つあり戻り値が1つのように見える
だが、Elmの関数の引数は常に1つだけ、と
String.padLeft 9 '.' "not"
(((String.padLeft 9) '.') "not")
関数を使ってる側もこのように書けるし、引数が常に一つであることがわかる
- JSよりもシンプルとか
- フレンドリーな関数型言語とか
- パーフェクトバグレポートとか
言うことでかいし毎度看板に偽り無しと今回も感じた
Evanのフレーズを選ぶセンスの良さも素晴らしい