はじめに
Haskell を勉強中なのですが、($) と (.) の使い分けで少し混乱しました。というもの、読んでいた入門書で、($) と (.) が置き換え可能そうな例が載っていたからです。
置き換え可能なのか、私が勘違いしているだけなのかわからなかったため、調べていました。
前提
($) について
- 「関数適用演算子」という名前です
- ($) による関数適用は右結合になります
- これは、($) が Haskell 標準の二項演算子の中で最も低い優先順位であることと、($) が右結合であることから実現されています(参照:演算子 - ウォークスルー Haskell)
- 定義は以下の通り
($) :: (a -> b) -> a -> b f $ x = f x
- ($) 演算子についてわかりやすく説明している記事を見つけました
(.) について
- (.) の正式名称は調べても出てこないのですが、あえて呼ぶなら「関数合成演算子」のようになるようです
- その名の通り、関数合成に使われます
- 定義は以下の通り
(.) :: (b -> c) -> (a -> b) -> a -> c f . g = \x -> f (g x)
置き換え可能な例
例えば、以下のような例で引っかかりました。以下の2つのコードは、両方とも180を返し、同じ挙動をしているように見えます
product $ map (*3) $ zipWith max [1,2] [4,5]
product . map (*3) $ zipWith max [1,2] [4,5]
結論
結論は、「置き換え可能なことも多い」「($) と (.) の定義を考えれば、置き換えられる場合とそうでない場合が見分けられるようになる」だと思います
以下の記事が詳しくてわかりやすかったです。幾つもの具体例を挙げて説明してくださっており、「置き換えられる場合」と「置き換えられない場合」の違いに気づくことができました。私が説明し直すよりわかりやすいかと思うので、同じところで詰まった方は、是非以下の記事を読んでみてください。「($)のNGケース」と「(.)のNGケース」のセクションに、置き換えられない場合の理由が載っています
結局は、($) と (.) 自身やその前後の関数の型で不整合が起きてエラーが出るため置き換えられない、という当たり前の結論になりそうです
終わりに
「置き換えられそうだ」という直感が部分的には合っていたことがわかってよかったです。しかし、直感のままだと置き換えられない場合の理由を説明できないため、綺麗に整理して説明してくださっている記事に出会えて助かりました