学習の素材は、
すごいHaskellたのしく学ぼう!
今回は、教材に載っていない自分の意見・考えが多い為、番外扱いとしています。
関数もアプリカティブ
関数 (->) r
もアプリカティブであり、インスタンス宣言されている。
instance Applicative ((->) a) where
pure = const
(<*>) f g x = f x (g x)
const
は一つ目の引数で与えられた値を必ず返す関数。 よって、 pure 1
が返す関数は、どんな引数を取っても1を返す。
関数をアプリカティブファンクターのインスタンスとして考える
おさらいとして、アプリカティブファンクターの型クラス定義は次のようになっている。
class Functor f => Applicative (f :: * -> *) where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
なので、関数に特殊化すると pure
、 <*>
はそれぞれ次のように書ける。
pure :: a -> (r -> a)
(<*>) :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
関数の場合、アプリカティブ値の中身とはなんぞやということになるのだが、ここでは 関数の計算結果 を指すのだろう。よって、関数の計算結果を、与えられた関数で写すことになる。写された中身が最終的に値になるには、r
型の引数が必要になる。
関数に対して、続けざまにアプリカティブ値の中の関数を適用する
例として 次のコードは、508
を返す。
(+) <$> (+3) <*> (*100) $ 5 --508
インスタンス宣言の定義にある(<*>) f g x = f x (g x)
それぞれをに当てはめてみると、
(\a -> (+) ((+3) a)) 5 ((\b -> (*100) b) 5)
ということになる。
(+) <$> (+3)
はfmap (+) (+3)
であり、\x -> (+) ((+3) x)
となる。
アプリカティブな関数のインスタンスが何をやっているかを、変形後の式から読み解いてみると・・。
- まず左辺は、引数をとって、引数に3を加算した結果を左辺に持つセクションを返す(
Num a => a -> a -> a
)。 - 次に右辺。こちらは、単純に引数を100倍して返す関数を返す(
Num a => a -> a
)。 -
<*>
は、この左辺の最初の引数と、右辺の引数に5を与える。すると、それぞれセクション((+8)
)と値(500
)になる。このセクションが値に適用され、508
が求まる。
まとめ
インスタンス宣言だけ見ても意味を取ることが難しいが、Applicative
型クラス定義を関数に特殊化して具体化していくと、何をやっているかがつかめてくる。また、<*>
は関数合成 (.) :: (b -> c) -> (a -> b) -> a -> c
と少し似ているが、左辺が2引数関数である点が異なっていて、結果も変わってくる。
アプリカティブな関数が何の役に立つのか学習教材の中でも明確に書かれていないものの、アプリカティブな関数のインスタンス (->) r
が面白いことはわかった。