diff --git a/21-traversable/21-traversable.md b/21-traversable/21-traversable.md new file mode 100644 index 0000000..393eaf8 --- /dev/null +++ b/21-traversable/21-traversable.md @@ -0,0 +1,169 @@ +# Chapter 21 Traversable + +## 21.3 sequenceA + +```haskell +-- Why: (fmap . fmap) sum Just [1, 2, 3] = Just 6 + +-- Let's check the types +fmap :: Functor f => (a -> b) -> f a -> f b +(fmap . fmap) :: (Functor f1, Functor f2) => (a -> b) -> f1 (f2 a) -> f1 (f2 b) +(fmap . fmap) sum :: (Num a, Foldable t, Functor f1, Functor f2) => + f1 (f2 (t a)) -> f1 (f2 a) +(fmap . fmap) sum Just :: -- Just :: b -> Maybe b + -- Just :: (->) b (Maybe b) + -- for this to correspond with f1 (f2 (t a)) + -- f1 is '(->) b' + -- f2 is Maybe + -- b is 't a' + -- so f1 (f2 a) is (->) (t a) (Maybe a) + (Num a, Foldable t) => t a -> Maybe a + +-- A good way to see this is that we are lifting the 'sum' over the function +-- functor and the Maybe functor produced as a result of the function functor +-- +-- Just :: (->) a (Maybe a) +-- +-- And we are lifting (sum :: Foldable t => t a -> a) over (->) a and Maybe. +-- Notice that the a in Just needs to be specialized in a t a to function as +-- the input for sum, i.e +-- +-- Just' :: Foldable t => (->) (t a) (Maybe (t a)) +-- +-- We can now apply (fmap . fmap) sum and get: +-- (fmap . fmap) sum Just :: Foldable t => (->) (t a) (Maybe (t a)) + +-- It might be helpful to look at the generalized form: +-- +-- (fmap . fmap) h1 h2 :: ? +-- +(fmap . fmap) :: (Functor f1, Functor f2) => (a -> b) -> f1 (f2 a) -> f1 (f2 b) +(fmap . fmap) h1 :: (Functor f1, Functor f2) => f1 (f2 a) -> f1 (f2 b) +-- if we now want to add h2, with h2 being: b' -> c', we can deduce that +-- (->) b' is the f1 functor and c' is 'f2 a' +(fmap . fmap) h1 :: Functor f => (b' -> f a) -> (b' -> f b) +-- A couple of points to remember: +-- b' is an input of h2 and a is an input of h1, so h2 needs to be a function +-- that return returns an input from h1 in a functor (e.g. with Just this can +-- take a 'Foldable t => t a' and returns a Maybe (t a)) +-- This implies that there are restrictions imposed on h2: +-- h2 needs to return a functor which contains an input type of h1 +-- Applying h2 just gives: +(fmap . fmap) h1 h2 :: (Functor f) => b' -> f b + +-- Does this restriction on h2 hold for 'Just'? +-- Consider that sum :: Foldable t => t a -> a +-- And Just :: a -> Maybe a, with a being more generic than 't a', we can +-- indeed say that the resulting a in 'Maybe a' can be of type 't a'. + +-- We can rewrite (fmap . fmap) h1 h2 as: +-- +-- fmap h1 . h2 +-- +-- So (fmap . fmap) sum Just can be written as fmap sum . Just + +-- Another example with different types accross the board: +foo :: String -> Maybe [Bool] +foo [] = Just [] +foo (x:xs) = case foo xs of + Just xs -> case x of + 't' -> Just $ True : xs + 'f' -> Just $ False : xs + otherwise -> Nothing + otherwise -> Nothing + +bar :: [Bool] -> Integer +bar = foldr (\b c -> if b then c + 1 else c - 1) 0 + +-- where (fmap . fmap) bar foo = fmap bar . foo +``` + +## 21.4 traverse + +Why is traverse not defined as: + +`traverse :: (Functor f, Traversable t) => (a -> f b) -> t a -> f (t b)` + +Maybe let's ask ourselves why sequenceA cannot be defined as such: + +`sequenceA :: (Traversable t, Functor f) => t (f a) -> f (t a)` + +Let's try to implement it: + +```haskell +-- Without applicative, but functor allowed +instance Traversable [] where + sequenceA [] = ??? -- we have no way of putting this in a functor, we + -- would need pure at least... + sequenceA (x:xs) = ??? -- we have an x :: f a + -- and an xs :: [f a] + -- We can turn the xs into f [a] via sequenceA: + -- sequenceA xs, but now we need to concatenate + -- 'f a' and 'f [a]' with only using fmap and this + -- is impossible. We can fmap (:) into 'f a', but + -- then we need (<*>) to apply this to 'f [a]' + +-- with applicative it works: +instance Traversable [] where + sequenceA [] = pure [] + sequenceA (x:xs) = (:) <$> x <*> sequenceA xs +``` + +## 21.6 morse code revisited + +Why `(sequence .) . fmap` and not `sequence . fmap` + +```haskell +fmap :: Functor f => (a -> b) -> (f a -> f b) +sequence :: (Monad m, Traversable t) => t (m a) -> m (t a) +(.) :: (b -> c) -> (a -> b) -> (a -> c) +(.) sequence :: (Monad m, Traversable t) => (a -> t (m b)) -> (a -> m (t b)) +(.) sequence fmap :: -- a :: (a' -> b') + -- t (m b) :: (->) (f a') (f b') + -- t :: (->) (f a') i.e. (->) (f a') must be Traversable + -- m b :: f b' i.e f shoulde be a Monad + (Monad m, Traversable (->) (f a)) => + (a -> b) -> m (m a -> b) +-- Which is not really wat we want... +-- However, if you check (.) sequence, then it looks close to what we want +(.) ((.) sequence) :: (Monad m, Traversable t) => + (a -> (b -> t (m c))) -> (a -> (b -> m (t c))) +(.) ((.) sequence) fmap :: -- a = (a' -> b') + -- b -> t (m c) = (f a') -> f b' + -- b = f a' + -- t (m c) = f b' + -- t = f (A traversable t is also a functor, see + -- its definition) + -- b' :: m c + (Monad m, Traversable t) => + (a' -> m c) -> (t a' -> m (t c)) +``` + +## 21.10 Traversable Laws + +```haskell +Compose :: f (g a) -> Compose f g a +f :: Applicative f => a -> f b +g :: Applicative g => a -> f b -- bascause of traverse g +fmap g :: (Applicative f1, Functor f2) => f2 a -> f2 (f1 b) +fmap g . f :: (Applicative f1, Applicative f2) => a -> f2 (f1 b) +Compose . (fmap g . f) :: (Applicative f1, Applicative f2) => + a -> Compose f g b +traverse (Compose . fmap g . f) :: + (Applicative f1, Applicative f2, Traversable t) => + t a -> Compose f1 f2 (t b) + +traverse f :: (Applicative f, Traversable t) => t a -> f (t b) +fmap (traverse g) :: (Applicative f1, Applicative f2, Traversable t) => + f1 (t a) -> f1 (f2 (t b)) +fmap (traverse g) . traverse f :: + (Applicative f1, Applicative f2, Traversable t) => + t a -> f1 (f2 (t b)) +Compose . fmap (traverse g) . traverse f :: + (Applicative f1, Applicative f2, Traversable t) => + t a -> Compose f1 f2 (t b) +``` + +## 21.12 Chapter Exercises + +see [src/chapter.hs](./src/chapter.hs) \ No newline at end of file diff --git a/21-traversable/21.12-chapter-exercises.md b/21-traversable/21.12-chapter-exercises.md deleted file mode 100644 index 02acf4a..0000000 --- a/21-traversable/21.12-chapter-exercises.md +++ /dev/null @@ -1,2 +0,0 @@ -# Chapter Exercises -see src/chapter.hs \ No newline at end of file diff --git a/21-traversable/src/chapter.hs b/21-traversable/src/chapter.hs index 676c381..595c704 100644 --- a/21-traversable/src/chapter.hs +++ b/21-traversable/src/chapter.hs @@ -78,7 +78,7 @@ instance Foldable (Three a b) where foldMap f (Three _ _ c) = f c instance Traversable (Three a b) where traverse f (Three a b c) = Three a b <$> f c -instance (Arbitrary a, Arbitrary b, Arbitrary c) => +instance (Arbitrary a, Arbitrary b, Arbitrary c) => Arbitrary (Three a b c) where arbitrary = Three <$> arbitrary <*> arbitrary <*> arbitrary instance (Eq a, Eq b, Eq c) => EqProp (Three a b c) where (=-=) = eq @@ -124,14 +124,14 @@ data S n a = S (n a) a deriving (Eq, Show) instance Functor n => Functor (S n) where fmap f (S n a) = S (fmap f n) (f a) instance Foldable n => Foldable (S n) where - foldMap f (S n a) = foldMap f n <> f a + foldMap f (S n a) = foldMap f n <> f a instance Traversable n => Traversable (S n) where traverse f (S n a) = S <$> traverse f n <*> f a instance (Functor n, Arbitrary (n a), Arbitrary a) => Arbitrary (S n a) where arbitrary = S <$> arbitrary <*> arbitrary -- The EqProp instance from the book fails. No idea why, the logic seems -- sound? --- instance (Applicative n, Testable (n Property), EqProp a) => +-- instance (Applicative n, Testable (n Property), EqProp a) => -- EqProp (S n a) where -- (S x y) =-= (S p q) = (property $ (=-=) <$> x <*> p) .&. (y =-= q) instance (Eq (n a), Eq a) => EqProp (S n a) where (=-=) = eq @@ -153,7 +153,7 @@ instance Foldable Tree where instance Traversable Tree where traverse _ Empty = pure Empty traverse f (Leaf a) = Leaf <$> f a - traverse f (Node t a t') = + traverse f (Node t a t') = Node <$> (traverse f t) <*> f a <*> traverse f t' -- probably not the best distribution for a tree. -- Should probably have it have a certain depth similar to [] @@ -169,7 +169,7 @@ instance (Eq a) => EqProp (Tree a) where (=-=) = eq -- fmap :: Functor f => (a -> b) -> f a -> f b -- foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m --- foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b +-- foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b -- traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b) main :: IO ()