webネタ

Webエンジニアが業務に関係することをメモしていく

すごいHaskell本のメモ

FunctorやらMonadまわりのメモ。

Functor

箱と比喩されている。リストやMaybeなど大体Functor。

fmapをもってる。(Scalaでいうmap)

fmap :: Functor f => (a -> b) -> f a -> f b

aをとりbを返す関数と、Functor値aをとる。aに関数を適用してFunctor値bを返す。

Prelude> fmap (+1) (Just 1)
Just 2

しかしFunctorだと中の関数に何かしようとすると辛くなる。

Prelude> let a = fmap (+) (Just 1)

Prelude> :t a
a :: Maybe (Integer -> Integer) 部分適用(カリー)されてMaybeの中に関数が入っている

Prelude> fmap (\f -> f 1) a
Just 2

これを簡単にできるのが↓

Applicative

文脈と比喩されている。リストやMaybeもまたApplicative。

pureと<*>を持っている。

Prelude> import Control.Applicative

pureは、aを受け取りapplicative aを返すだけの関数。

Prelude Control.Applicative> :t pure
pure :: Applicative f => a -> f a

<*>は

Prelude Control.Applicative> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Appricativeに入った関数を受け取るところがFunctorと違う。

Prelude Control.Applicative> Just (+1) <*> Just 1
Just 2

functorで辛かった部分をapplicativeで書くと、

Prelude Control.Applicative> fmap (+) (Just 1) <*> Just 1
Just 2

短く書くこともできる↓

Prelude Control.Applicative> (+) <$> (Just 1) <*> Just 1
Just 2

簡単に書けた。

関数のApplicative

関数の<*>は、

f <*> g = \x -> f x (g x) : fとgの関数を受け取り、g xした結果とxをfに渡す関数を返す

Prelude Control.Applicative> (+) <$> (+1) <*> (+1) $ 1
4

??

(+) <$> (+1) = (1 + a) + b
(+) <$> (+1) <*> (+1) = \x -> (1 + x) + (1 + x)
これに1を渡すと、4

たしかに4だ。

リストのApplicative

Prelude Control.Applicative> (+) <$> [1,2,3] <*> [2,3,4]
[3,4,5,4,5,6,5,6,7]

それぞれに適用される。

ペアで適用したいときは、

Prelude Control.Applicative> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [2,3,4]
[3,5,7]

liftA2

Prelude Control.Applicative> :t liftA2
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c

aとbをとりcを返す関数と、Applicative値aとApplicative値bをとり、Applicative値cを返す関数。

Prelude Control.Applicative> liftA2 (+) (Just 1) (Just 2)
Just 3

↓より↑のほうが分かりやすい。

Prelude Control.Applicative> (+) <$> (Just 1) <*> (Just 2)
Just 3

Monoid

Monadとは別物。

結合法則

ok : 1 + (2 + 3) == (1 + 2) + 3 (++や*など)
ng : 1 - (2 - 3) == (1 - 2) - 3
みたいに順番変わってもokなもの

単位元

リストなら[]。(+)なら0。など

Prelude> import Data.Monoid
Prelude Data.Monoid> [1,2] `mappend` [3,4]
[1,2,3,4]
Prelude Data.Monoid> "ab" `mappend` "cd"
"abcd"

その他いろいろ

-- Sum, Product
Prelude Data.Monoid> (Sum 1) `mappend` (Sum 2)
Sum {getSum = 3}
Prelude Data.Monoid> (Product 2) `mappend` (Product 2)
Product {getProduct = 4}
Prelude Data.Monoid> getProduct $ (Product 2) `mappend` (Product 2)
4

-- Any, All
Prelude Data.Monoid> (Any True) `mappend` (Any False)
Any {getAny = True}
Prelude Data.Monoid> (All True) `mappend` (All False)
All {getAll = False}

-- compareは左側がEQのときに右側を評価
Prelude Data.Monoid> LT `mappend` GT
LT
Prelude Data.Monoid> EQ `mappend` GT
GT

Foldable

畳み込むアレ

Prelude Data.Monoid> import Data.Foldable as F

Prelude Data.Monoid F> :t F.foldl
F.foldl :: Foldable t => (a -> b -> a) -> a -> t b -> a

Prelude Data.Monoid F> F.foldl (+) 0 [1,2,3]
6

Prelude Data.Monoid F> F.foldl (+) 1 (Just 1)
2

Monad

噂のアレ

Applicativeのupgrade版

-- pureと同じようにreturnがある
Prelude> :t return
return :: Monad m => a -> m a

-- バインドと呼ばれる。scalaでいうflatMap。
Prelude> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

Prelude> Just 1 >>= \x -> Just (x + 1)
Just 2

Prelude> [1,2,3] >>= \x -> [x + 1]
[2,3,4]

Prelude> [1,2,3] >>= \x -> if x > 1 then [x] else []
[2,3]

Prelude> Just 1 >>= \x -> Just (x+x) >>= \x -> Just (x*x) >>= \x -> if x > 10 then Just x else Nothing
Nothing

-- Scalaだと
scala> Some(1) flatMap { x => Some(x+x) } flatMap { x => Some(x*x) } flatMap { x => if (x > 10) Some(x) else None }
res6: Option[Int] = None

『あーScalaのアレと同じか』というのがちょいちょいあって助かる。