Chapter 25
{-# LANGUAGE InstanceSigs #-}
module Chapter25 where
import Data.Bifunctor
import Test.Hspec
newtype Compose f g a = Compose
{ getCompose :: f (g a)
} deriving (Eq, Show)
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose fga) = Compose $ (fmap . fmap) f fga
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
pure :: a -> Compose f g a
pure = Compose . pure . pure
(<*>) :: Compose f g (a -> b) -> Compose f g a -> Compose f g b
Compose fgab <*> Compose fga = Compose $ fmap (<*>) fgab <*> fga
instance (Foldable f, Foldable g) => Foldable (Compose f g) where
foldMap :: Monoid m => (a -> m) -> Compose f g a -> m
foldMap f (Compose fga) = (foldMap . foldMap) f fga
instance (Traversable f, Traversable g) => Traversable (Compose f g) where
traverse :: Applicative m => (a -> m b) -> Compose f g a -> m (Compose f g b)
traverse f (Compose fga) = Compose <$> (traverse . traverse) f fga
data Deux a b =
Deux a
b
deriving (Eq, Show)
instance Bifunctor Deux where
bimap f g (Deux a b) = Deux (f a) (g b)
newtype Const a b =
Const a
deriving (Eq, Show)
instance Bifunctor Const where
bimap f _ (Const a) = Const (f a)
data Drei a b c =
Drei a
b
c
deriving (Eq, Show)
instance Bifunctor (Drei a) where
bimap f g (Drei a b c) = Drei a (f b) (g c)
data SuperDrei a b c =
SuperDrei a
b
deriving (Eq, Show)
instance Bifunctor (SuperDrei a) where
bimap f _ (SuperDrei a b) = SuperDrei a (f b)
newtype SemiDrei a b c =
SemiDrei a
deriving (Eq, Show)
instance Bifunctor (SemiDrei a) where
bimap _ _ (SemiDrei a) = SemiDrei a
data Quadriceps a b c d =
Quadzzz a
b
c
d
deriving (Eq, Show)
instance Bifunctor (Quadriceps a b) where
bimap f g (Quadzzz a b c d) = Quadzzz a b (f c) (g d)
data Or a b
= Fst a
| Snd b
deriving (Eq, Show)
instance Bifunctor Or where
bimap f _ (Fst a) = Fst (f a)
bimap _ g (Snd b) = Snd (g b)
spec :: SpecWith ()
spec = do
describe "Applicatives" $
it "can be composed" $ do
pure (+ 2) <*> pure 3 `shouldBe` Compose [Just 5]
pure (+ 2) <*> pure 3 `shouldBe` Compose (Just [5])
Compose [Just (+ 2), Nothing, Just (* 3)] <*>
Compose [Just 4, Nothing] `shouldBe`
Compose [Just 6, Nothing, Nothing, Nothing, Just 12, Nothing]
Compose (Just [(+ 2), (* 3)]) <*>
Compose (Just [4, 5]) `shouldBe` Compose (Just [6, 7, 12, 15])
describe "Compose Instances" $ do
it "foldable" $ do
foldMap show (Compose [Just 1, Nothing, Just 2]) `shouldBe` "12"
foldMap show (Compose (Just [1, 2, 3])) `shouldBe` "123"
foldMap show (Compose Nothing :: Compose Maybe [] Int) `shouldBe` ""
it "traversable" $ do
sequenceA (Compose [Just [1], Nothing, Just [2]]) `shouldBe`
[Compose [Just 1, Nothing, Just 2]]
sequenceA (Compose (Just [Just 1, Just 2, Just 3])) `shouldBe`
(Just $ Compose (Just [1, 2, 3]))
sequenceA (Compose (Just [Just 1, Nothing, Just 2])) `shouldBe` Nothing
describe "Bifunctor Instances" $ do
it "Deux" $ do
first (++ "world") (Deux "hello" 1) `shouldBe` Deux "helloworld" 1
second (+ 5) (Deux "hello" 1) `shouldBe` Deux "hello" 6
bimap (++ "world") (+ 5) (Deux "hello" 1) `shouldBe` Deux "helloworld" 6
it "Const" $ do
first (++ "world") (Const "hello") `shouldBe` Const "helloworld"
second (+ 5) (Const "hello") `shouldBe` Const "hello"
bimap (++ "world") (+ 5) (Const "hello") `shouldBe` Const "helloworld"
it "Drei" $ do
first (++ "world") (Drei (Just 1) "hello" 1) `shouldBe`
Drei (Just 1) "helloworld" 1
second (+ 5) (Drei (Just 1) "hello" 1) `shouldBe` Drei (Just 1) "hello" 6
bimap (++ "world") (+ 5) (Drei (Just 1) "hello" 1) `shouldBe`
Drei (Just 1) "helloworld" 6
it "SuperDrei" $ do
first (++ "world") (SuperDrei (Just 1) "hello") `shouldBe`
SuperDrei (Just 1) "helloworld"
second (+ 5) (SuperDrei (Just 1) "hello") `shouldBe`
SuperDrei (Just 1) "hello"
bimap (++ "world") (+ 5) (SuperDrei (Just 1) "hello") `shouldBe`
SuperDrei (Just 1) "helloworld"
it "SemiDrei" $ do
first (++ "world") (SemiDrei (Just 1)) `shouldBe` SemiDrei (Just 1)
second (+ 5) (SemiDrei (Just 1)) `shouldBe` SemiDrei (Just 1)
bimap (++ "world") (+ 5) (SemiDrei (Just 1)) `shouldBe` SemiDrei (Just 1)
it "Quadzzz" $ do
first (++ "world") (Quadzzz "test" (Just 1) "hello" 1) `shouldBe`
Quadzzz "test" (Just 1) "helloworld" 1
second (+ 5) (Quadzzz "test" (Just 1) "hello" 1) `shouldBe`
Quadzzz "test" (Just 1) "hello" 6
bimap (++ "world") (+ 5) (Quadzzz "test" (Just 1) "hello" 1) `shouldBe`
Quadzzz "test" (Just 1) "helloworld" 6
it "Or" $ do
first (++ "world") (Fst "hello") `shouldBe`
(Fst "helloworld" :: Or String Int)
first (++ "world") (Snd 1) `shouldBe` Snd 1
second (+ 5) (Fst "hello") `shouldBe` Fst "hello"
second (+ 5) (Snd 1) `shouldBe` (Snd 6 :: Or String Int)
bimap (++ "world") (+ 5) (Fst "hello") `shouldBe` Fst "helloworld"
bimap (++ "world") (+ 5) (Snd 1) `shouldBe` Snd 6