I'm having the time of my life reading the wonderful Haskell Programming from first principles and I came by the following example that I'm just not able to take apart (Page 1286 e-reader):
Prelude> (fmap . fmap) sum Just [1, 2, 3]
Just 6
It is obvious to me how the following works:
Prelude> fmap sum $ Just [1,2,3]
Just 6
And I already manually deconstructed (fmap . fmap)
to understand how the types work. But when thinking about this as "lifting twice" it doesn't make sense, since I'm lifting over both the Just and List data constructors.
I typed out the following in ghci
:
Prelude> :t (fmap . fmap)
(fmap . fmap)
:: (Functor f, Functor f1) => (a -> b) -> f1 (f a) -> f1 (f b)
Prelude> :t (fmap . fmap) sum
(fmap . fmap) sum
:: (Num b, Foldable t, Functor f, Functor f1) =>
f1 (f (t b)) -> f1 (f b)
Prelude> :t (fmap . fmap) sum Just
(fmap . fmap) sum Just :: (Num b, Foldable t) => t b -> Maybe b
I don't understand how to derive the last output. When feeding (fmap . fmap) sum
the Just
data constructor, How does the compiler know to replace both f1
and f
for Maybe
? After I'll get a good answer here, how could I have figured it out myself?
That isn't lifting over both Maybe
and List
(that would be (fmap . fmap) sum (Just [1,2,3])
, which has a type problem), but over the function type (->)
and Maybe
.
Just :: a -> Maybe a
-- ((->) a) (Maybe a)
-- f (g a) for f ~ ((->) a) and g ~ Maybe
(fmap . fmap) :: (a -> b) -> f (g a ) -> f (g b)
-- Num x => ([x] -> x) -> f (g [x]) -> f (g x)
-- Num x => ([x] -> x) -> ([x] -> Maybe [x]) -> [x] -> Maybe x
-- ^ ^ ^
-- sum Just [1,2,3]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With