Ink

Contents related to tech, hobby, etc

~.~ と ~<$>~ の順序のメモ

|

.<$> の順序のメモ

Haskellにおいてしょっちゅうお世話になる演算子二人組を連れてきたよ!

infixr 9 .
(.) :: (b -> c) -> (a -> b) -> a -> c

infixl 4 <$>
(<$>) :: Functor f => (a -> b) -> f a -> f b

...でもたまに分からなくなるよ。

考えたいケース

以下はまだわかる

(+ 1) <$> [1..5]

でも以下のように書くと、 "Why NOT?" とか言って書き換えを提案されるよ!

(* 2) <$> (+ 1) <$> [1..5]

-- why not
(* 2) . (+ 1) <$> [1..5]

Found: print <$> (++ " ") <$> getLine Why not: print . (++ " ") <$> getLine

で、素直にこれで書き換えられるのも分かるのだけど きちんと論理的に考えてみるのです。

論理的に考えるあるよ

(* 2) . (+ 1) <$> [1..5]

解釈を考えるあるよ。基本的に左から読まれるわけだけど(ほんとか?)、 今回は中置演算子で繋がれているので infix の値を確認するよ。

infix は、 値が大きければ大きい程優先される 。 冒頭でも書いたけど、 srchaskell{(.)} は infix 9 で srchaskell{(<$>)} は infix 4

なので、先に . での束縛が行われるよ。つまり:

((* 2) . (+ 1)) <$> [1..5]

こうだね。 今度はそれぞれの型を考えてみるよ。

(* 2) :: Num a => a -> a
(+ 1) :: Num a => a -> a
(.) :: (b -> c) -> (a -> b) -> a -> c

(* 2) . :: Num a' => (a -> a') -> a -> a'
(* 2) . (+ 1) :: Num a' => a' -> a'

これを <$> と一緒にしてみると

(* 2) . (+ 1) :: Num a' => a' -> a'
(<$>) :: Functor f => (a -> b) -> f a -> f b

(* 2) . (+ 1) <$> :: (Num a', Functor f) => f a' -> f a'

なるほど。ここで晴れて [1..5] を適用できる。

(* 2) . (+ 1) <$> [1..5] :: Num a' => [a']

わーい!

まとめ(?)

型パズル、単純に楽しかった!