I am reading "Category Theory for Programmers" By Bartosz Milewski and on pg. 164 he introduces a natural transformation for the Reader Functor:
newtype Reader e a = Reader (e -> a)
instance Functor (Reader e) where
fmap f (Reader g) = Reader (\x -> f (g x))
I am having a difficult time understanding the type signature of this transformation.
My best guess is something like:
fmap :: (a -> b) -> (Reader e -> a) -> (Reader e -> b)
I am confused that the functor is declared with Reader partially applied Reader e and then on line two there f and g which are the two function arguments. Then, there is a lambda expression with x. Since there is no value for x, I assume the Reader(\x -> ....) lambda expression is partially applied. Does x refer to some outside value? Where does e come into play?
So, how does the fmap instance satisfy fmap => (a->b) -> f a -> f b?
CodePudding user response:
No, here f ~ Reader e, so f a means Reader e a, and thus:
fmap @ Reader e :: (a -> b) -> Reader e a -> Reader e b
It thus will with a function f :: (a -> b) transform a Reader e a to a Reader e b.
Now for the pattern (Reader g), we know that g has type g :: e -> a, we thus need to produce a function e -> b. We can do that by creating a function that maps a variable x to g x, and then applies f on that result, such that \x -> f (g x) or shorter f . g has type f . g :: e -> b, which thus satisfies the type constraint.
CodePudding user response:
First, there are two separate constructors named Reader:
- The type constructor with kind
Type -> Type -> Type - The data constructor with type
(a -> b) -> Reader a b.
Reader e is a partial application of the type constructor to get something with kind Type -> Type, which is suitable for defining a Functor instance.
Reader g is a pattern that matches a value of type Reader e a, which means g is bound to a value of type e -> a.
In order to construct a value of type Reader e b, we need to construct a value of type e -> b. We don't have access to a value of type b for that function to return, but we can get one using f :: a -> b. So, our function of type e -> b will take its e argument, pass it to g :: e -> a, and we'll pass that result to f.
It's slightly easier to see when you expand Reader to the function types, and write them in prefix (rather than infix) order.
fmap :: (a -> b) -> Reader e a -> Reader e b
-- by isomorphism of Reader e x and e -> x
:: (a -> b) -> (e -> a) -> (e -> b)
-- using prefix notation
:: (a -> b) -> ((->) e) a -> ((->) e) b
-- generalizing from a specific functor to an abstract functor
:: (a -> b) -> f a -> f b
where f is the functor (->) e or Reader e whose instance we are defining.
