| 
 
I hate the environment functor
 Here we have the well-known fmapfunction:     fmap :: Functor f => (a -> b) -> f a -> f b
 It takes a single function and a (collection of input values /
decorated input value / something something input value) and produces
a (collection of output values / decorated output value / something
something output value). Yow, that's not going to work.  Is there any good terminology for a
value of type f awhenfis an arbitrary functor?  A while back I
discussed a similar
problem and suggested
the term “mote” for a value in a monadic type.  I will try calling anf tvalue a “tparcel and see how that works.  So[t],Maybe t, andIO tare all examples oftparcels, in
various functors. Starting over then.  Here we have the well-known fmapfunction:     fmap :: Functor f => (a -> b) -> f a -> f b
 It takes a single function, and an aparcel, and produces abparcel, by applying the function independently to theavalues in
the parcel. Here is a sort of reversed version of fmapthat I callpamf:     pamf :: Functor f => f (a -> b) -> a -> f b
 It takes a parcel of functions, and a single input and produces a
parcel of outputs, by applying each function in the parcel
independently to the single avalue.  It can be defined in terms offmap:     pamf fs a = fmap ($ a) fs
 So far so good.  Now I ask you to predict the type of     pamf fmap
 Certainly it should start out with     pamf fmap :: (Functor f, Functor g) => ...
 because the pamfand thefmapmight be operating in two different
functors, right?  Indeed, if I compose the functions the other way
around,fmap pamf, the type does begin this way; it is:     (Functor f, Functor g) => f (g (a -> b)) -> f (a -> g b)
 The fhere is the functor in whichfmapoperates, and thegis
the functor in whichpamfis operating.  In generalfmaptakes an
arbitrary function               a       ->      b
 and lifts it to a new function that operates in the ffunctor:             f a       ->    f b
 Here it has taken pamf, which is a function           g (a -> b)  ->     (a -> g b)
 and lifted it to a new function that operates in the ffunctor:        f (g (a -> b))  ->  f (a -> g b)
 This is complicated but straightforward.  Okay, that was fmap pamf.
What aboutpamf fmapthough? The computed type is         pamf fmap :: Functor f => f a -> (a -> b) -> f b
 and when I saw this I said “What.  Where did ggo?  What happened tog?” Then I paused and for a while and said “… I bet it's that goddamn
environment thing again.”  Yep, that's what it was.  It's the
environment functor, always turning up where I don't want it and
least expect it, like that one guy we all went to college with.  The
environment functor, by the way, is yet another one of those things
that Haskell ought to have a standard name for, but doesn't.  The
phrase “the reader monad” is fairly common, but here I only want the
functor part of the monad.  And people variously say “reader monad”,
“environment monad”, and “evaluation monad” to mean the same thing.
In this article, it will be the environment functor. Here's what happened.  Here are fmapandpamfagain:     fmap :: Functor f => (p -> q) -> f p -> f q
    pamf :: Functor g => g (a -> b) -> a -> g b
 The first argument to pamfshould be a parcel in thegfunctor.
Butfmapis not a parcel, sopamf fmapwill be a type error,
right?  Wrong!  If you are committed enough, there is a way to
construe any function as a parcel.  A functionp -> qis aqparcel in the environment functor.  Say thatgdenotes an
environment functor.  In this functor, a parcel of typeg tis a
function which consults an “environment” of typeeand yields a
result of typet.  That is, $$g\ t \equiv e \to t.$$ When operating in the environment functor, fmaphas the type(a ->
b) -> g a -> g b, which is shorthand for(a -> b) -> (e -> a) -> (e
-> b).  This instance offmapis defined this way:     fmap f x = \e -> f (x e)
 or shorter and more mysteriously     fmap = (.)
 which follows by η-reduction, something Haskell enthusiasts never seem
to get enough of. In fmap f x, thexisn't the actual value to give tof; instead
it's a parcel, as it always is withfmap.  In the context of the
environment functor,xis a function that consults the environmenteand returns ana.  The result offmap f xis a new parcel: it
usesxto consult the supplied environment for a value of typea,
which it then feeds tofto get the required value of typeb. In the application pamf fmap, the left sidepamfwantsfmapto
be a parcel.  But it's not a parcel, it's a function.  So, type error,
right?  No!  Any function is a parcel if you want it to be, it's a
parcel in the environment functor!  Andfmapis a function:     fmap :: Functor f => (p -> q) -> f p -> f q
 so it can be understood as a parcel in the environment functor, where
the environment ehas typep -> q.  Thenpamfis operating in
this environment functor, so $$g\ t = (p \to q) \to t.$$ Ag tparcel
is a function that consults an “environment” of typep -> qand
somehow produces atvalue.  (Haskell folks, who are obsessed with
currying all the things, will write this as the
nearly unreadableg = ((->) (p -> q)).) We wanted pamfto have this type:     pamf :: Functor g =>            g (a -> b)  -> a ->            g b
 and since Haskell has decided that gmust be the environment functor
with !!g\ x \equiv (p \to q) \to x!!,
this is an abbreviation for:     pamf ::              ((p -> q) -> (a -> b)) -> a -> ((p -> q) -> b)
 To apply this to fmap, we have to unify the type ofpamf's
argument, which is(p -> q) -> (a -> b), and the type offmap,
which is(p -> q) -> (f p -> f q).  Then !!a\equiv f\ p!! and !!b
\equiv f\ q!!, so the result ofpamf fmapis     pamf fmap :: Functor f => f p -> ((p -> q) -> f q)
 Where did ggo?  It was specialized to mean the environment functor((->) (p -> q)), so it's gone. The funny thing about the type of pamf fmapis that it is exactly
the type offlip fmap, which isfmapwith the order of its two
arguments reversed:    (flip fmap) x f ≡ fmap f x
 and indeed, by some theorem or other, because the types are identical,
the functions themselves must be identical also!  (There are some side
conditions, all of which hold here.) The two functions pamf fmapandflip fmapare identical.  Analogous to the wayfmap, restricted
to the environment functor, is identical to(.),pamf, when
similarly restricted, is exactlyflip.  You can even see this from its type:     pamf :: ((p -> q) -> (a -> b)) -> a -> ((p -> q) -> b)
 Or, cleaning up some superfluous parentheses and inserting some new ones:     pamf :: ((p -> q) ->  a -> b) -> (a ->  (p -> q) -> b)
 And putting !!c = p\to q!!:     pamf :: (c        -> a -> b) -> (a -> c        -> b)
    flip :: (                 the same                 )
 Honestly, I would have preferred a type error: “Hey, dummy, fmaphas
the wrong type to be an argument topamf, which wants a functorial
value.”  Instead I got “Okay, if you want functions to be a kind of
functor I can do that, also wouldn't it be simpler if the universe was
two-dimensional and there were only three kinds of quarks?  Here you
go, no need to thank me!”  Maybe someone can explain to me why this is
a useful behavior, and then explain why it is so useful that it should
happen automatically and implicitly instead of being triggered
by some lexical marker like:     newtype Environment e a = Environment (e -> a)
    instance Functor (Environment e) where
      fmap f (Environment x) = Environment $ \e -> f (x e)
 I mean, seriously, suppose you wrote a + bwherebwas
accidentally a function instead of a number.  What if when you did
that, instead of a type error, Haskell would silently shift into some
restricted domain in which it could implicitly interpretbas a
number in some weird way and give you something totally bizarre?
Isn't the whole point of Haskell supposed to be that it doesn't
implicitly convert things that way? [ Addendum 20181111: Apparently, everyone else hates it too. ] 
 
[Other articles in category /prog/haskell] 
permanent link
 
 |