LoginSignup

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 5 years have passed since last update.

Elmでは関数の戻り値は1つだけで引数も1つだけ

Last updated at Posted at 2016-12-05

Elm Advent Calendar 2016 - Adventar 5日目

"... every Elm function takes only one argument"

Richard Feldman, Elm in Action MEAP V03
長年の疑問が一気に解決された :joy:

Elmの戻り値は一つだけ

こっちはeasy partだった
Elmが式であり、式は評価されると1つの値を返す

  • early returnできない
  • そもそもreturnがない

等々の「そこから導かれる話」には面白いものがあり、勉強になった。
が、基本easy part

Elmの関数型定義で->が複数回登場するパターン

長い間うまく理解できてない(自分でうまく説明できない)ことの1つ

update : Msg -> Model -> ( Model, Cmd Msg )
viewEntries : String -> List Entry -> Html Msg

こういう関数の型定義の「読み方」がわからなかった
腑に落ちる説明に出会うこともなかった

  • ->はただのセパレータ?
    • それだと、引数と戻り値の境界がゆるふわすぎる
  • MsgModel が引数なんだったら、Msg Model -> ( Model, Cmd Msg )、という風に書くのが自然では?
    • ->の前に引数、後ろが戻り値
    • 引数複数とるなら、`Msg Model -> ... と関数定義してるときのように並べる
    • しかし、そういう風になってないのなら、何か理由があるはず :fearful:
  • 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.17v0.18に追随できてないので、半分はdeprecatedになってる
  • Haskellとか関数型言語を使いこなすFunctional polyglotにによる説明よりもとっつきやすい
  • ここでホストされてる記事は何度か読んだが基本的にどれも勉強になった
  • とはいえ、->は今ひとつだった
    • 「セパレータです」という説明しかしてない
    • 、複数->が登場する関数型定義を取り上げた箇所では、「->は go toと発音する」とかなんだかよく分からない説明(MsgModelへ gotoする?)

Elmの関数の引数は1つだけ

という状況を救ってくれたのが、Richardの見事な解説

「複数の引数をもつ関数の型定義」というセクション見出しから予想してた内容とは別物だった

Richardの解説のポイント

StringモジュールのpadLeft

> String.padLeft 9 '.' "not"
"......not" : String

という使い方をする、3つ引数を渡してる。いわゆるleftpad系のヘルパー関数
この関数に対して、Richardは関数の部分適用を使いながら、型定義を作り上げる

まずpadLeft9だけを渡す

> String.padLeft 9
<function> : Char -> String -> String

エラーにならない
部分適用して返された関数をpadNineと定義する

padNine = String.padLeft 9

このpadNineに今後は '.'だけ渡す

> padNine '.'
<function> : String -> String

返された関数をpadNineDotsを定義する

padNineDots = padNine '.'

ここでpadNinepadNineDotsの型定義だが

padNine: Char -> (なんらかの関数)
padNineDots: String -> String
  • 1文字(Char)受け取って関数返す関数
  • String受け取りString返す関数

となるので、padLeftの型定義をpadNinepadNineDotsの型定義をもとに作ると

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のフレーズを選ぶセンスの良さも素晴らしい

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up