parent
c6779197b1
commit
854e37c476
6 changed files with 408 additions and 0 deletions
@ -0,0 +1,6 @@ |
|||||||
|
# Chapter Exercises |
||||||
|
## Writing and testing monad instances |
||||||
|
see src/instances.hs |
||||||
|
|
||||||
|
## Writing functions |
||||||
|
see src/functions.hs |
@ -0,0 +1,54 @@ |
|||||||
|
module EitherMonad where |
||||||
|
|
||||||
|
import Test.QuickCheck |
||||||
|
import Test.QuickCheck.Checkers |
||||||
|
import Test.QuickCheck.Classes |
||||||
|
|
||||||
|
data Sum a b = First a | Second b deriving (Eq, Show) |
||||||
|
|
||||||
|
instance Functor (Sum a) where |
||||||
|
fmap _ (First a) = First a |
||||||
|
fmap f (Second b) = Second (f b) |
||||||
|
|
||||||
|
instance Applicative (Sum a) where |
||||||
|
pure = Second |
||||||
|
(<*>) _ (First a) = First a |
||||||
|
(<*>) (First a) _ = First a |
||||||
|
(<*>) (Second f) (Second b) = Second (f b) |
||||||
|
|
||||||
|
instance Monad (Sum a) where |
||||||
|
return = pure |
||||||
|
(First a) >>= _ = First a |
||||||
|
(Second b) >>= k = k b |
||||||
|
|
||||||
|
instance (Arbitrary a, Arbitrary b) => Arbitrary (Sum a b) where |
||||||
|
arbitrary = do |
||||||
|
a <- arbitrary |
||||||
|
b <- arbitrary |
||||||
|
elements [First a, Second b] |
||||||
|
|
||||||
|
instance (Eq a, Eq b) => EqProp (Sum a b) where |
||||||
|
(=-=) = eq |
||||||
|
|
||||||
|
type SumType = Sum String (Int,Int,Int) |
||||||
|
|
||||||
|
main :: IO () |
||||||
|
main = do |
||||||
|
quickBatch (monad (undefined :: SumType)) |
||||||
|
|
||||||
|
-- ******************************** |
||||||
|
-- Associativity |
||||||
|
-- (m >>= f) >>= g |
||||||
|
-- join (fmap g (join (fmap f m))) |
||||||
|
|
||||||
|
-- We can't just do |
||||||
|
-- m >>= (f >>= g) because f is not of type (Monoid m => m b) |
||||||
|
-- We want to pass an m to an h |
||||||
|
-- m >>= h |
||||||
|
-- where h is to be determined, we know it is of form (Monoid m => a -> m b) |
||||||
|
-- and it should be based on f >>= g, this doesn't work, but we could apply |
||||||
|
-- f to an `a` and provide that to the: |
||||||
|
-- h x = f x >>= g |
||||||
|
-- This is can be done via anonymous function: |
||||||
|
-- m >>= (\x -> f x >>= g) |
||||||
|
-- join (fmap (\x -> join (fmap g (f x))) m) |
@ -0,0 +1,6 @@ |
|||||||
|
module Bind where |
||||||
|
|
||||||
|
import Control.Monad (join) |
||||||
|
|
||||||
|
bind :: Monad m => (a -> m b) -> m a -> m b |
||||||
|
bind = join . fmap f |
@ -0,0 +1,124 @@ |
|||||||
|
module Examples where |
||||||
|
|
||||||
|
import Control.Monad (join) |
||||||
|
|
||||||
|
twiceWhenEven :: [Integer] -> [Integer] |
||||||
|
twiceWhenEven xs = do |
||||||
|
x <- xs |
||||||
|
if even x |
||||||
|
then [x*x,x*x] |
||||||
|
else [x*x] |
||||||
|
|
||||||
|
twiceWhenEven' :: [Integer] -> [Integer] |
||||||
|
twiceWhenEven' xs = |
||||||
|
xs >>= \x -> if even x then [x*x,x*x] else [x*x] |
||||||
|
|
||||||
|
twiceWhenEven'' :: [Integer] -> [Integer] |
||||||
|
twiceWhenEven'' xs = |
||||||
|
join (fmap (\x -> if even x then [x*x,x*x] else [x*x]) xs) |
||||||
|
|
||||||
|
|
||||||
|
data Cow = Cow { name :: String, age :: Int, weight :: Int } |
||||||
|
deriving (Eq, Show) |
||||||
|
|
||||||
|
noEmpty :: String -> Maybe String |
||||||
|
noEmpty "" = Nothing |
||||||
|
noEmpty s = Just s |
||||||
|
|
||||||
|
noNegative :: Int -> Maybe Int |
||||||
|
noNegative n | n >= 0 = Just n |
||||||
|
| otherwise = Nothing |
||||||
|
|
||||||
|
weightCheck :: Cow -> Maybe Cow |
||||||
|
weightCheck c = |
||||||
|
let w = weight c |
||||||
|
n = name c |
||||||
|
in if (n == "Bess") && (w > 499) |
||||||
|
then Nothing |
||||||
|
else Just c |
||||||
|
|
||||||
|
mkSphericalCow :: String -> Int -> Int -> Maybe Cow |
||||||
|
mkSphericalCow name' age' weight' = |
||||||
|
case noEmpty name' of |
||||||
|
Nothing -> Nothing |
||||||
|
Just n -> |
||||||
|
case noNegative age' of |
||||||
|
Nothing -> Nothing |
||||||
|
Just a -> |
||||||
|
case noNegative weight' of |
||||||
|
Nothing -> Nothing |
||||||
|
Just w -> weightCheck $ Cow n a w |
||||||
|
|
||||||
|
mkSphericalCow' :: String -> Int -> Int -> Maybe Cow |
||||||
|
mkSphericalCow' name' age' weight' = do |
||||||
|
n <- noEmpty name' |
||||||
|
a <- noNegative age' |
||||||
|
w <- noNegative weight' |
||||||
|
weightCheck $ Cow n a w |
||||||
|
|
||||||
|
mkSphericalCow'' :: String -> Int -> Int -> Maybe Cow |
||||||
|
mkSphericalCow'' name' age' weight' = |
||||||
|
noEmpty name' >>= \n -> |
||||||
|
noNegative age' >>= \a -> |
||||||
|
noNegative weight'>>= \w -> |
||||||
|
weightCheck $ Cow n a w |
||||||
|
|
||||||
|
mkSphericalCow''' :: String -> Int -> Int -> Maybe Cow |
||||||
|
mkSphericalCow''' name' age' weight' = |
||||||
|
join (fmap f $ noEmpty name') |
||||||
|
where f n = join (fmap g $ noNegative age') |
||||||
|
where g a = join (fmap h $ noNegative weight') |
||||||
|
where h w = weightCheck $ Cow n a w |
||||||
|
|
||||||
|
-- Some explanation: |
||||||
|
-- Starting at the end with the function h: |
||||||
|
-- h :: Int -> Maybe Cow |
||||||
|
|
||||||
|
-- We know g takes and Int (as the `a` given is used in the construction of |
||||||
|
-- Cow). `h` is fmapped over a Maybe Int, giving us a Maybe (Maybe Cow) |
||||||
|
-- (fmap :: (Int -> Maybe Cow) -> Maybe Int -> Maybe (Maybe Cow)) |
||||||
|
-- Using join, reduces this to just a Maybe Cow, so this makes: |
||||||
|
-- g :: Int -> Maybe Cow |
||||||
|
|
||||||
|
-- And looking at f, we see that it takes a String (n is used as String). |
||||||
|
-- `g` is mapped over a Maybe Int, giving us a Maybe (Maybe Cow)) |
||||||
|
-- (fmapp :: (Int -> Maybe Cow) -> Maybe Int -> Maybe (Maybe Cow)) |
||||||
|
-- Using join reduces this to just a Maybe Cow to, so we have: |
||||||
|
-- f :: String -> Maybe Cow |
||||||
|
|
||||||
|
-- Finally `f` is mapped over a Maybe String, giving us a Maybe (Maybe Cow) |
||||||
|
-- (fmap :: (String -> Maybe Cow) -> Maybe String -> Maybe (Maybe Cow)) |
||||||
|
-- Using join, reduces this to jsut a Maybe Cow, this gives us the |
||||||
|
-- Maybe Cow |
||||||
|
|
||||||
|
-- mkSphericalCow''' :: String -> Int -> Int -> Maybe Cow |
||||||
|
-- mkSphericalCow''' name' age' weight' = z name' age' weight' |
||||||
|
-- where z :: String -> Int -> Int -> Maybe Cow |
||||||
|
-- z n a w = join (fmap (f w a) $ noEmpty n) |
||||||
|
-- f :: Int -> Int -> String -> Maybe Cow |
||||||
|
-- f w a n = join (fmap (g w n) $ noNegative a) |
||||||
|
-- g :: Int -> String -> Int -> Maybe Cow |
||||||
|
-- g w n a = join (fmap (h n a) $ noNegative w) |
||||||
|
-- h :: String -> Int -> Int -> Maybe Cow |
||||||
|
-- h n a w = weightCheck $ Cow n a w |
||||||
|
|
||||||
|
f :: Integer -> Maybe Integer |
||||||
|
f 0 = Nothing |
||||||
|
f n = Just n |
||||||
|
|
||||||
|
g :: Integer -> Maybe Integer |
||||||
|
g i = if even i then (Just (i+1)) else Nothing |
||||||
|
|
||||||
|
h :: Integer -> Maybe String |
||||||
|
h i = Just ("10191" ++ show i) |
||||||
|
|
||||||
|
doSomething' :: Integer -> Maybe (Integer, Integer, String) |
||||||
|
doSomething' n = do |
||||||
|
a <- f n |
||||||
|
b <- g a |
||||||
|
c <- h b |
||||||
|
pure (a, b, c) |
||||||
|
|
||||||
|
doSomething'' :: Integer -> Maybe (Integer, Integer, String) |
||||||
|
doSomething'' n = |
||||||
|
(pure g) <*> (f n) |
@ -0,0 +1,58 @@ |
|||||||
|
module Functions where |
||||||
|
|
||||||
|
-- 1 |
||||||
|
j :: Monad m => m (m a) -> m a |
||||||
|
j x = x >>= id |
||||||
|
|
||||||
|
-- 2 |
||||||
|
l1 :: Monad m => (a -> b) -> m a -> m b |
||||||
|
-- without fmap |
||||||
|
l1 f x = do |
||||||
|
a' <- x |
||||||
|
return $ f a' |
||||||
|
l1 f x = x >>= (return . f) |
||||||
|
-- with fmap :) |
||||||
|
-- l1 = fmap |
||||||
|
|
||||||
|
-- 3 |
||||||
|
l2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c |
||||||
|
-- without fmap |
||||||
|
l2 f x y = do |
||||||
|
a' <- x |
||||||
|
b <- y |
||||||
|
return $ f a' b |
||||||
|
-- l2 f x y = x >>= (\a -> y >>= \b -> return $ f a b) |
||||||
|
-- with fmap |
||||||
|
-- l2 f x y = (fmap f x) <*> y |
||||||
|
|
||||||
|
-- 4 |
||||||
|
a :: Monad m => m a -> m (a -> b) -> m b |
||||||
|
-- without fmap |
||||||
|
a x fs = do |
||||||
|
a' <- x |
||||||
|
f <- fs |
||||||
|
return $ f a' |
||||||
|
-- with fmap |
||||||
|
-- a x fs = do |
||||||
|
-- f <- fs |
||||||
|
-- fmap f x |
||||||
|
-- a x fs = fs >>= \y -> fmap y x |
||||||
|
-- a x fs = fs <*> x |
||||||
|
|
||||||
|
-- 5 |
||||||
|
meh :: Monad m => [a] -> (a -> m b) -> m [b] |
||||||
|
meh [] _ = return [] |
||||||
|
meh (x:xs) f = do |
||||||
|
b <- f x |
||||||
|
fmap (b:) $ meh xs f |
||||||
|
-- using flipType as base |
||||||
|
-- meh as f = flipType (fmap f as) |
||||||
|
|
||||||
|
-- 6 |
||||||
|
flipType :: (Monad m) => [m a] -> m [a] |
||||||
|
flipType = flip meh id |
||||||
|
-- and without using meh |
||||||
|
-- flipType [] = return [] |
||||||
|
-- flipType (x:xs) = do |
||||||
|
-- x' <- x |
||||||
|
-- fmap (x':) $ flipType xs |
@ -0,0 +1,160 @@ |
|||||||
|
module Instances where |
||||||
|
|
||||||
|
import Test.QuickCheck |
||||||
|
import Test.QuickCheck.Checkers |
||||||
|
import Test.QuickCheck.Classes |
||||||
|
|
||||||
|
-- import Control.Monad (join) |
||||||
|
import Control.Applicative (liftA2) |
||||||
|
|
||||||
|
-- 1 |
||||||
|
data Nope a = NopeDotJpg deriving (Eq, Show) |
||||||
|
instance Functor Nope where |
||||||
|
fmap _ _ = NopeDotJpg |
||||||
|
instance Applicative Nope where |
||||||
|
pure _ = NopeDotJpg |
||||||
|
(<*>) _ _ = NopeDotJpg |
||||||
|
instance Monad Nope where |
||||||
|
return _ = NopeDotJpg |
||||||
|
(>>=) _ _ = NopeDotJpg |
||||||
|
instance Arbitrary (Nope a) where |
||||||
|
arbitrary = return NopeDotJpg |
||||||
|
instance EqProp (Nope a) where |
||||||
|
(=-=) = eq |
||||||
|
type NopeType = Nope (Int,Int,Int) |
||||||
|
|
||||||
|
-- 2 |
||||||
|
data PhhhbbtttEither b a = Left' a | Right' b deriving (Eq, Show) |
||||||
|
instance Functor (PhhhbbtttEither b) where |
||||||
|
fmap _ (Right' b) = Right' b |
||||||
|
fmap f (Left' a) = Left' (f a) |
||||||
|
instance Monoid b => Applicative (PhhhbbtttEither b) where |
||||||
|
pure = Left' |
||||||
|
(Right' b) <*> (Right' b') = Right' $ b `mappend` b' |
||||||
|
(Right' b) <*> _ = Right' b |
||||||
|
_ <*> (Right' b) = Right' b |
||||||
|
(Left' f) <*> (Left' a) = Left' (f a) |
||||||
|
instance Monoid b => Monad (PhhhbbtttEither b) where |
||||||
|
return = Left' |
||||||
|
(Right' b) >>= _ = Right' b |
||||||
|
(Left' a) >>= k = k a |
||||||
|
instance (Arbitrary a, Arbitrary b) => Arbitrary (PhhhbbtttEither b a) where |
||||||
|
arbitrary = do |
||||||
|
a <- arbitrary |
||||||
|
b <- arbitrary |
||||||
|
oneof [return $ Left' a, return $ Right' b] |
||||||
|
instance (Eq a, Eq b) => EqProp (PhhhbbtttEither b a) where |
||||||
|
(=-=) = eq |
||||||
|
type PhhhbbtttEitherType = PhhhbbtttEither String (Int,Int,Int) |
||||||
|
|
||||||
|
-- 3 |
||||||
|
newtype Identity a = Identity a deriving (Eq, Ord, Show) |
||||||
|
instance Functor Identity where |
||||||
|
fmap f (Identity a) = Identity $ f a |
||||||
|
instance Applicative Identity where |
||||||
|
pure = Identity |
||||||
|
(Identity f) <*> (Identity a) = Identity $ f a |
||||||
|
instance Monad Identity where |
||||||
|
return = Identity |
||||||
|
(Identity a) >>= k = k a |
||||||
|
instance Arbitrary a => Arbitrary (Identity a) where |
||||||
|
arbitrary = arbitrary >>= return . Identity |
||||||
|
instance Eq a => EqProp (Identity a) where |
||||||
|
(=-=) = eq |
||||||
|
type IdentityType = Identity (Int,Int,Int) |
||||||
|
|
||||||
|
-- 4 |
||||||
|
data List a = Nil | Cons a (List a) deriving (Eq, Show) |
||||||
|
instance Functor List where |
||||||
|
fmap _ Nil = Nil |
||||||
|
fmap f (Cons a l) = Cons (f a) (fmap f l) |
||||||
|
instance Monoid (List a) where |
||||||
|
mempty = Nil |
||||||
|
mappend l Nil = l |
||||||
|
mappend Nil l = l |
||||||
|
mappend (Cons a l) l' = Cons a (mappend l l') |
||||||
|
instance Applicative List where |
||||||
|
{-# INLINE pure #-} |
||||||
|
pure = flip Cons Nil |
||||||
|
{-# INLINE (<*>) #-} |
||||||
|
Nil <*> _ = Nil |
||||||
|
_ <*> Nil = Nil |
||||||
|
(Cons f fl) <*> as = (fmap f as) `mappend` (fl <*> as) |
||||||
|
instance Monad List where |
||||||
|
return = flip Cons Nil |
||||||
|
Nil >>= _ = Nil |
||||||
|
(Cons a l) >>= k = (k a) `mappend` (l >>= k) |
||||||
|
|
||||||
|
-- I originally had the above, because I thought that you could not |
||||||
|
-- use join, but I found a solution online that uses join. Trying to |
||||||
|
-- understand: |
||||||
|
-- join is defined as (join x = x >>= id) |
||||||
|
-- You might think, wait a minute? id is (a -> a) and thus doesn't fit the |
||||||
|
-- bill of (a -> m b), but let's look at things more closely |
||||||
|
-- |
||||||
|
-- (>>=) :: m a -> (a -> m b) -> m b |
||||||
|
-- a here is the type of x :: (m a') |
||||||
|
-- b here is the inner type of x :: a', |
||||||
|
-- updating gives us: |
||||||
|
-- (>>=) :: m (m a') -> (m a' -> m a') -> m a' |
||||||
|
-- and we see that we can pass the id function to it |
||||||
|
|
||||||
|
-- This turns the >>= definition into a recursive one where the second time |
||||||
|
-- the function is the id, which will join it all |
||||||
|
|
||||||
|
-- unfortunately this crashes when testing the monad laws ... Why? |
||||||
|
-- because this never ends... You get |
||||||
|
-- fmap id (fmap id (... (fmap id (fmap f as)))) |
||||||
|
-- as >>= f = join $ fmap f as |
||||||
|
|
||||||
|
-- toList :: [a] -> List a |
||||||
|
-- toList [] = Nil |
||||||
|
-- toList (x:xs) = Cons x $ toList xs |
||||||
|
|
||||||
|
replicateM' :: Applicative m => Int -> m a -> m (List a) |
||||||
|
replicateM' cnt0 f = loop cnt0 |
||||||
|
where loop cnt |
||||||
|
| cnt <= 0 = pure Nil |
||||||
|
| otherwise = liftA2 Cons f (loop (cnt - 1)) |
||||||
|
|
||||||
|
listOf' :: Gen a -> Gen (List a) |
||||||
|
listOf' gen = sized $ \n -> |
||||||
|
do k <- choose (0,n) |
||||||
|
replicateM' k gen |
||||||
|
|
||||||
|
|
||||||
|
instance Arbitrary a => Arbitrary (List a) where |
||||||
|
arbitrary = listOf' arbitrary |
||||||
|
-- arbitrary = fmap toList (listOf arbitrary) |
||||||
|
-- arbitrary = do |
||||||
|
-- a <- arbitrary |
||||||
|
-- l <- arbitrary |
||||||
|
-- frequency [(1, return Nil), (3, return $ Cons a l)] |
||||||
|
instance Eq a => EqProp (List a) where |
||||||
|
(=-=) = eq |
||||||
|
type ListType = List (Int,Int,Int) |
||||||
|
|
||||||
|
main :: IO () |
||||||
|
main = do |
||||||
|
quickBatch (functor (undefined :: NopeType)) |
||||||
|
quickBatch (applicative (undefined :: NopeType)) |
||||||
|
quickBatch (monad (undefined :: NopeType)) |
||||||
|
|
||||||
|
quickBatch (functor (undefined :: PhhhbbtttEitherType)) |
||||||
|
quickBatch (applicative (undefined :: PhhhbbtttEitherType)) |
||||||
|
quickBatch (monad (undefined :: PhhhbbtttEitherType)) |
||||||
|
|
||||||
|
quickBatch (functor (undefined :: IdentityType)) |
||||||
|
quickBatch (applicative (undefined :: IdentityType)) |
||||||
|
quickBatch (monad (undefined :: IdentityType)) |
||||||
|
|
||||||
|
-- [a] |
||||||
|
quickBatch (functor (undefined :: [(Int,Int,Int)])) |
||||||
|
quickBatch (monoid (undefined :: [(Int,Int,Int)])) |
||||||
|
quickBatch (applicative (undefined :: [(Int,Int,Int)])) |
||||||
|
quickBatch (monad (undefined :: [(Int,Int,Int)])) |
||||||
|
|
||||||
|
quickBatch (functor (undefined :: ListType)) |
||||||
|
quickBatch (monoid (undefined :: ListType)) |
||||||
|
quickBatch (applicative (undefined :: ListType)) |
||||||
|
quickBatch (monad (undefined :: ListType)) |
Loading…
Reference in new issue