fugafuga.write

日々のログ

すごいH本 part76

アプリカティブの便利な関数

Control.ApplicativeliftA2という関数がある。

liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b

1つの関数を2つのアプリカティブ値に適用する関数。 これを、

liftA2 :: (Applicative f) => (a -> b -> c) -> (f a -> f b -> f c)

のように解釈すると、

通常の2引数関数を、2つのアプリカティブ値を引数に取る関数に昇格させる関数

とみなすことができる。

2つのアプリカティブ値からそれらの返り値をリストとして内包する1つのアプリカティブ値を組み立てることができる。

Just 3Just 4 を使って実践する。

まず、Just 4Just [4] にする。

*Main Control.Applicative> fmap (\x -> [x]) (Just 4)
Just [4]

Just [3,4] を作る

*Main Control.Applicative> liftA2 (:) (Just 3) (Just [4])
Just [3,4]

これと同じ

*Main Control.Applicative> (:) <$> (Just 3) <*> (Just [4])
Just [3,4]

アプリカティブ値のリストをとって、 リストを返り値として持つ1つのアプリカティブ値を返す関数を作る

sequenceA' :: (Applicative f) => [f a] -> f [a]
sequenceA' [] = pure []
sequenceA' (x:xs) = (:) <$> x <*> sequenceA' xs

実行

*Main Control.Applicative> sequenceA' [Just 1, Just 2]
Just [1,2]

こう書くのと同値

*Main Control.Applicative> (:) <$> Just 1 <*> sequenceA' [Just 2]
Just [1,2]

さらに評価を進めるとこう

*Main Control.Applicative> (:) <$> Just 1 <*> ((:) <$> Just 2 <*> sequenceA' [])
Just [1,2]

sequenceA' []Just [] になる

*Main Control.Applicative> (:) <$> Just 1 <*> ((:) <$> Just 2 <*> Just [])
Just [1,2]

最終的にこう

*Main Control.Applicative> (:) <$> Just 1 <*> Just [2]
Just [1,2]

sequenceA' は畳み込みを使っても実装できる

sequenceA'' :: (Applicative f) => [f a] -> f [a]
sequenceA'' = foldr (liftA2 (:)) (pure [])

実行

*Main Control.Applicative> sequenceA'' [Just 3, Just 2, Just 1]
Just [3,2,1]
*Main Control.Applicative> sequenceA'' [Just 3, Nothing, Just 1]
Nothing
*Main Control.Applicative> sequenceA'' [(+3),(+2),(+1)] 3
[6,5,4]
*Main Control.Applicative> sequenceA'' [[1,2,3],[4,5,6]]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
*Main Control.Applicative> sequenceA'' [[1,2,3],[4,5,6],[3,4,4],[]]
[]

Maybe 値のリストに対して使うと、すべての返り値をリストとして含んだ単一のMaybe値ができる。 ただし、元のリストの中のMaybe値のいずれかがNothingだった場合、結果もNothingになる。

これを使う場面として、 Maybe値のリストがあり、要素のすべてがNothingでない場合に限ってなにかしたい場合に使う。

*Main Control.Applicative> map (\f -> f 7) [(>4),(<10),odd]
[True,True,True]
*Main Control.Applicative> and $ map (\f -> f 7) [(>4),(<10),odd]
True

sequenceA'' を使う

*Main Control.Applicative> sequenceA'' [(>4),(<10),odd] 7
[True,True,True]
*Main Control.Applicative> and $ sequenceA'' [(>4),(<10),odd] 7
True

sequenceA を [] に対して使うと、リストのリストをとり、リストのリストを返す関数になる。

*Main Control.Applicative> sequenceA [[1,2,3],[4,5,6]]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
*Main Control.Applicative> [[x,y] | x <- [1,2,3], y <- [4,5,6]]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]

I/O アクションに対して使う

*Main Control.Applicative> sequenceA [getLine, getLine, getLine]
hey
foo
hoo
["hey","foo","hoo"]

所感

最後の方が難しい。理解しきれてない部分がある。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る