Chapter 22


{-# LANGUAGE InstanceSigs #-}

module Chapter22 where

import Control.Applicative
import Data.Char
import Data.Maybe
import Data.Monoid
import Test.Hspec
import Test.Hspec.Checkers
import Test.QuickCheck
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes

cap :: String -> String
cap = map toUpper

rev :: String -> String
rev = reverse

composed :: String -> String
composed = cap . reverse

fmapped :: String -> String
fmapped = cap <$> reverse

tupled :: String -> (String, String)
tupled = (,) <$> cap <*> rev

tupled' :: String -> (String, String)
tupled' = do
  capped <- cap
  reversed <- rev
  return (capped, reversed)

tupled'' :: String -> (String, String)
tupled'' = cap >>= (\capped -> rev >>= (\reversed -> return (capped, reversed)))

newtype Reader r a = Reader
  { runReader :: r -> a
  }

instance Functor (Reader r) where
  fmap :: (a -> b) -> Reader r a -> Reader r b
  fmap f (Reader ra) = Reader $ f . ra

instance Applicative (Reader r) where
  pure :: a -> Reader r a
  pure a = Reader $ const a
  (<*>) :: Reader r (a -> b) -> Reader r a -> Reader r b
  Reader rab <*> Reader ra = Reader $ \r -> rab r (ra r)

instance Monad (Reader r) where
  return = pure
  (>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b
  Reader ra >>= aRb = Reader $ \r -> runReader (aRb (ra r)) r

ask :: Reader a a
ask = Reader id

myLiftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
myLiftA2 f fa fb = f <$> fa <*> fb

asks :: (r -> a) -> Reader r a
asks = Reader

newtype HumanName =
  HumanName String
  deriving (Eq, Show)

newtype DogName =
  DogName String
  deriving (Eq, Show)

newtype Address =
  Address String
  deriving (Eq, Show)

data Person = Person
  { humanName :: HumanName
  , dogName :: DogName
  , address :: Address
  } deriving (Eq, Show)

data Dog = Dog
  { dogsName :: DogName
  , dogsAddress :: Address
  } deriving (Eq, Show)

rob :: Person
rob = Person (HumanName "Rob") (DogName "Chewie") (Address "Chicago")

getDogRM :: Person -> Dog
getDogRM = runReader reader
  where
    reader =
      Reader dogName >>= (\dn -> Reader address >>= \adr -> return $ Dog dn adr)

getDogRM' :: Person -> Dog
getDogRM' = runReader reader
  where
    reader = do
      dn <- Reader dogName
      adr <- Reader address
      return $ Dog dn adr

x = [1, 2, 3]

y = [4, 5, 6]

z = [7, 8, 9]

xs = lookup 3 $ zip x y

ys = lookup 6 $ zip y z

zs = lookup 4 $ zip x y

z' :: Integer -> Maybe Integer
z' n = lookup n $ zip x z

x1 = liftA2 (,) xs ys

x2 = liftA2 (,) ys zs

x3 = liftA2 (,) z' z'

summed :: Num c => (c, c) -> c
summed = uncurry (+)

bolt :: Integer -> Bool
bolt = liftA2 (&&) (> 3) (< 8)

sequA :: Integral a => a -> [Bool]
sequA = sequenceA [(> 3), (< 8), even]

s' = summed <$> ((,) <$> xs <*> ys)

spec :: SpecWith ()
spec = do
  describe "cap and rev" $ do
    it "composed" $ composed "Julie" `shouldBe` "EILUJ"
    it "fmapped" $ fmapped "Julie" `shouldBe` "EILUJ"
    it "tupled" $ tupled "Julie" `shouldBe` ("JULIE", "eiluJ")
    it "tupled'" $ tupled' "Julie" `shouldBe` ("JULIE", "eiluJ")
    it "tupled''" $ tupled'' "Julie" `shouldBe` ("JULIE", "eiluJ")
  describe "ask" $
    it "is the identity reader" $ do
      runReader ask "Hi" `shouldBe` "Hi"
      runReader ask 1 `shouldBe` 1
  describe "reading comprehension" $ do
    it "myLiftA2" $ do
      myLiftA2 (+) [1, 2] [3, 4] `shouldBe` liftA2 (+) [1, 2] [3, 4]
      myLiftA2 (++) ["hello"] ["world"] `shouldBe`
        liftA2 (++) ["hello"] ["world"]
    it "asks" $ do
      runReader (asks (++ " there")) "Hi" `shouldBe` "Hi there"
      runReader (asks (* 3)) 4 `shouldBe` 12
    it "the Reader applicative" $ do
      runReader ((+ 1) <$> Reader (* 2)) 3 `shouldBe` 7
      runReader (pure 1) "hi" `shouldBe` 1
      runReader (Reader (++) <*> Reader (++ "World")) "Hello" `shouldBe`
        "HelloHelloWorld"
  describe "reading comprehension" $ do
    it "the Reader monad" $ do
      runReader (return 1) "hi" `shouldBe` 1
      runReader (Reader (++ "World") >>= (\a -> Reader (++ a))) "Hello" `shouldBe`
        "HelloHelloWorld"
    it "getDogRM using monad" $ do
      getDogRM rob `shouldBe` Dog (DogName "Chewie") (Address "Chicago")
      getDogRM' rob `shouldBe` Dog (DogName "Chewie") (Address "Chicago")
  describe "chapter exercises" $ do
    it "the example data" $ do
      xs `shouldBe` Just 6
      ys `shouldBe` Just 9
      zs `shouldBe` Nothing
    it "the example data with applicatives" $ do
      x1 `shouldBe` Just (6, 9)
      x2 `shouldBe` Nothing
      x3 3 `shouldBe` (Just 9, Just 9)
    it "helper functions" $ do
      summed (1, 3) `shouldBe` 4
      summed <$> ((,) <$> xs <*> ys) `shouldBe` Just 15
      summed <$> ((,) <$> xs <*> zs) `shouldBe` Nothing
      bolt 3 `shouldBe` False
      bolt 5 `shouldBe` True
      bolt 8 `shouldBe` False
      bolt <$> z `shouldBe` [True, False, False]
      foldMap All (sequA 5) `shouldBe` All False
      foldMap All (sequA 6) `shouldBe` All True
      sequA <$> s' `shouldBe` Just [True, False, False]
      bolt <$> ys `shouldBe` Just False