haskell

pureと<$>と<*>

こういうところこういうところでよく見るのですが、全くわからずにコピペして使ってきました。

この辺りにわかり易い説明があります。
qiitaの記事
お気楽Haskellプログラミング入門の記事

pure<*>は、Control.Applicativeのメソッドです。<$>あるいはfmapは、Data.Functorのメソッドです。<$>fmapの中置演算子版、要するに同じ関数です。

pureは、任意のデータを取ってApplicativeに持ち上げ(lift)ます。値でも関数でも構いません。

pure :: a -> f a

-- 例
>>> :t pure 1
pure 1 :: (Applicative f, Num a) => f a

>>> :t pure True
pure True :: Applicative f => f Bool

>>> :t pure (+2)
pure (+2) :: (Applicative f, Num a) => f (a -> a)

>>> :t pure (+)
pure (+) :: (Applicative f, Num a) => f (a -> a -> a)

>>> :t pure words
pure words :: Applicative f => f (String -> [String])

<*>は、Applicativeの中の値に対して関数を適用します。Applicativeに包まれた関数を第一引数にとり、Applicativeに包まれたデータを第二引数に取ります。

(<*>) :: f (a -> b) -> f a -> f b

例えば、Just 1は、Maybe Int型ですが、このIntをインクリメントしたい場合、以下のように書くかもしれません。

-- ghciで複数行定義をする場合も、インデントに気をつけて
Prelude> :set +m
Prelude> d = Just 1
Prelude> let d' = case d of
Prelude|            Just v -> return (v+1)
Prelude|            Nothing -> Nothing
Prelude|
Prelude> d'
Just 2

これを、<*>を使うと、以下のように簡潔に書くことができます。

Prelude> let d'' = pure (+1) <*> d
Prelude> d''
Just 2

MaybeはApplicativeのインスタンスです。pure (+1)で関数をApplicativeに持ち上げ、<*>を使ってMaybeの中の値に直接関数を適用することができるのです。もちろん、Nothingに適用しても大丈夫です。

Prelude> pure (+1) <*> Nothing
Nothing

またこれは、<$>を使うと以下のように書くこともできます。

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

Prelude> let d''' = (+1) <$> d
Prelude|
Prelude> d'''
Just 2

<$>は、関数をApplicativeの中のデータに適用します。見ての通り、<*><$>はとてもよく似ています。<*>は第一引数がApplicativeなのに対し、<$>は第一引数が通常の関数です。だから、(<*>) . pure<$>はほとんど同じです。

Prelude> :t (<*>) . pure
(<*>) . pure :: Applicative f => (a -> b) -> f a -> f b
Prelude> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b

全く同じだと思ったのですが、型を見てみると、少し違いました。関数の働き方は同じですが、前者のfはApplicativeで、後者はFunctorです。私はこの辺りのことがまだよくわかっていないので、説明できません。
Maybeの例の場合は、MaybeがApplicativeとFunctorの両方のインスタンスのため、全く同様に使うことができました。[]EitherIOなども全て、ApplicativeとFunctorの両方のインスタンスなので、上記の例と同じようなことができるはずです。

この辺りをちゃんと理解するには、Monad, Functor, Applicative, Monoidをまずわかっておく必要がありそうです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です