JavaScript
フロントエンド
Elm

なんかElmコードは引数書かないほどイケてるっぽい

趣味でElm触ってて、よく分かんないけどなんか動くみたいな感じでやってる、関数型素人のダイナミックHTMLコーダーである私目線の内容ですので、あまり深い内容ではないことを予めご了承ください:bow:

さて本題に入りますが、
JSのこんなコードがあるとします

const args = [1, 2]
const func = arg => arg * 2
args.map(func) // [2, 4]

これをElmで書くとこうなります

args = [1, 2]
func arg = arg * 2
List.map func args -- [2, 4]

JSでも引数1個の場合は()使わなくても関数定義できますが、
Elmだと何個でも()使わないですし、関数適用のときも使いません。
じゃあどこまでがその関数の区切りだと判別するんだよ、という話は、
どうやらElmコードは()使わないほどイケてるらしいをご参照ください。

次にJSでこんな感じで、

const args = [1, 2]
const func1 = arg => arg * 2
const func2 = arg => arg + 2
args.map(arg => func1(func2(arg))) // [6, 8]

無名関数使って関数合成するのは、Elmだとこう書けます。

args = [1, 2]
func1 arg = arg * 2
func2 arg = arg + 2
List.map (\arg -> func1 <| func2 arg) args -- [6, 8]

<|については上記記事にあります。)
\は無名関数の文法です。func arg = argfunc = \arg -> argは同じものみたいです。
話戻すと、こうは書けるんですが、熟練のElmersはこう書かないみたいなんですよね。
どうやら関数合成には並々ならぬこだわりがあるようで、それ用のスペシャルな関数が用意されています。

(<<) : (b -> c) -> (a -> b) -> a -> c

よくわからん記号の関数シリーズ。そしてこれも中置関数。なんかたぶん記号で関数定義すると中置できるっぽい。

でこれは、「任意型bを引数にとって任意型cを返す関数」と「任意型aを引数にとって同じ型bを返す関数」と「同じ型a」を引数にとって「同じ型c」を返す関数です。(あんま詳しくないですが型の見方も上記記事でちょっと説明してます。)

言っている意味はよくわからないが、とりあえず使ってみると、
(<<) func1 func2 1は、(\arg -> func1 <| func2 arg) 1になるみたいで、6が返ってきます。

これを中置したいんだけど、引数が3つあったらどこに中置すればいいんだろうって感じなんですが、
関数の部分適用ってやつがあるらしくて、この関数でいうと、b -> ca -> bの関数だけ渡すとa -> cの関数が返ってくるみたい。

つまりfunc1 << func2\arg -> func1 <| func2 argと同じようです。
さきほどmapに渡した関数と同じ形ですね。なのでこう書けます。

args = [1, 2]
func1 arg = arg * 2
func2 arg = arg + 2
List.map (func1 << func2) args -- [6, 8]

引数消えた!うーむ、不思議。けどなんかシンプルでいいかも。
ちなみにこの引数消せるシリーズは他にもあって、よく見かけるやつを紹介します。

(>>) : (a -> b) -> (b -> c) -> a -> c

逆版。
List.map (func2 >> func1) argsと書ける。

identity : a -> a

引数をそのまま返していいとき使う。JSで

const args = [1, 2]
args.map(arg => arg) // [1, 2]

これはElmだとこうだけど、

args = [1, 2]
List.map (\arg -> arg) args -- [1, 2]

identity使う方が通みたいです。

args = [1, 2]
List.map identity args -- [1, 2]

always : a -> b -> a

引数を使わないでいいとき使う。JSで

const args = [1, 2]
args.map(arg => 0) // [0, 0]

これはElmだとこうだけど、

args = [1, 2]
List.map (\arg -> 0) args -- [0, 0]

always使う方が通みたいです。

args = [1, 2]
List.map (always 0) args -- [0, 0]

まとめ

<<<|、つまり合成と適用って全然違うみたいですけど、私には何が違うのかよく分からないところに、私の頭脳の限界を感じていますが、
とりあえずモテりゃどうでもいいや。