Haskellの関数合成
$
と .
と ()
について
例として関数 f
と g
の型をそれぞれ以下のように定義して話を進める
f :: b -> c
g :: a -> b
($) :: (a -> b) -> a -> b
右に伸びるカッコの省略できるということが嬉しくて使ってる

右結合性を持つ
f $ g $ r == f (g r)
ちなみに、普通の関数適用は左結合である
f g r == (f g) r
関数の適用演算は結合律を満たさない
(f g) r != f (g r)
$
の優先順位は二項演算子の中で最低
(.) :: (b -> c) -> (a -> b) -> a -> c
例えば f . g
なら
f :: b -> c
、 g :: a -> b
なので
式全体で見れば f . g :: a -> c
を表す
右結合性を持つ
f . g . r == f . (g . r)
結合律を満たす
(f . g) . r == f . (g . r)
.
の優先順位は二項演算子の中で最高
ただし、関数適用よりは低い
print . show "42"
はエラーになる
print . (show "42")
と解釈されている
(print . show) "42"
とする必要がある
並べて書くと違いがほとんどないことに気付く
登場人物の関数たちが1引数関数というところが $
のときと違って意識するところ
なので途中に2引数関数がある場合は良い感じに1引数関数にしてやる必要がある
hsfoo p xs = sum $ filter p $ map (+1) xs -- `$`を使ったとき
foo p = sum . filter p . map (+1) -- `.`を使ったとき
$
はイメージ的には ()
の省略と同じ
何回か関数を実行してる感じ
hsfoo p xs = sum (filter p (map (+1) xs))
.
はでかい関数を作った後に一発実行してる感じ
hsfoo p xs = (でかい一つの関数) xs
.
で多引数関数にも対応する
ドットをいっぱい書けばいいらしい
h = (f.).g
.
に、2引数関数と1引数関数を与えると、2引数関数ができる
hs(.) :: (b -> c) -> (a -> b) -> a -> c
mappend :: Monoid a1 => a1 -> a1 -> a1 -- 2引数関数
f :: a -> a1 -- 1引数関数
(.) mappend :: Monoid a1 => (a -> a1) -> a -> a1 -> a1
(.) mappend f :: Monoid a1 => a -> a1 -> a1 -- 2引数関数
hs(|>) :: a -> (a -> b) -> b
a |> f = f a
書く順序と流れが一致する
pursには #
という関数がある
書き換えをしてみよう
(f . g) x
は、以下全てと同値
f (g x)
(f . g) $ x
f $ ( g $ x)
f . g $ x
f $ g $ x
f
, g
, x
という順番は変わらずに、あいだの演算子だけが変わってる
次に、「いくつか選択できる場合にどれを使うか?」という問題が生じる
つまり .
を使っていく
選べる場合は左から右に読めるようにする、とか
以下5つは全て同じ挙動になる
purs(hs)printStr ms = printList (flatTuples (toUnfoldable ms)))
printStr ms = ms # toUnfoldable # flatTuples # printList
printStr ms = printList $ flatTuples $ toUnfoldable ms
printStr = printList <<< flatTuples <<< toUnfoldable
printStr = toUnfoldable >>> flatTuples >>> printList
このケースでは、 ()
は個人的には論外としたい
#
か >>>
なら、左から右に読める
しかしHaskellerやpursマンは、右から左に読むことを特に苦と思っていないはず
むしろソッチの方が慣れている
だから、左から右に読めたとて、ふーんぐらいでしかない
>>>
か <<<
なら、ポイントフリーに書ける
実際に

が遭遇したのは以下のような状況
purs(hs)printStr (HashMap ms) = "{" <> (ms # toUnfoldable # flatTuples # printList) <> "}"
だからポイントフリースタイルは採用できない(したとてキモくなるので)
だから4つともほぼ平等に選択の余地がある
参考
初心者は ()
が多く、中級者で $
を使い、上級者は .
を使う
一段階目では、一番右側の括弧を消して、その始まりを $
に変える
つまり、一番右側には括弧は存在しなくなる
中間には存在し得る