The Functor
class contains a hidden second member:
class Functor f where
fmap :: (a -> b) -> f a -> f b
(GHC.Base.<$) :: a -> f b -> f a
Documentation:
Replace all locations in the input with the same value. The default definition is
fmap . const
, but this may be overridden with a more efficient version.
I would like to know more. Why this fmap . const
idiom is a separate member? How an alternative implementation might be more efficient? What are applications of this combinator?
It is included as a member to allow users to customize it for speed, and I guess because it makes it consistent with >>
.
I think it might be faster in the cases of the reader monad ((->) r)
.
x <$ _ = const x
vs
x <$ fa = fmap (const x) fa = (const x) . fa
although, this is really a question of compiler optimization. And, it does not appear to be defined for the reader monad in base.
It also might lead to a performance boost in strict collections. Namely
data Strict a = Strict !a
instance Functor Strict where
fmap f (Strict a) = Strict (f a)
x <$ _ = Strict x
this does not obey the functor laws, but nonetheless, you might want to do this in some situations.
A third example comes from infinite collections. Consider infinite lists
data Long a = Cons a (Long a)
instance Functor Long where
fmap f (Cons x xs) = Cons (f x) (fmap f xs)
which works fine, but think about
countUpFrom x = Cons x (countUpFrom (x+1))
ones = 1 <$ (countUpFrom 0)
now, with our definition that will expand to
ones = 1 <$ (countUpFrom 0)
= fmap (const 1) (countUpFrom 0)
= Cons (const 1 0) (fmap (const 1) (countUpFrom 1)
= Cons (const 1 0) (Cons (const 1 1) (fmap (const 1) (countUpFrom 2))
that is, it will allocate a whole bunch of Cons
cells as you walk this list. While, on the other hand, if you defined
x <$ _ = let xs = Cons x xs in xs
than
ones = 1 <$ countUpFrom 0
= let xs = Cons 1 xs in xs
which has tied the knot. An even more extreme example comes with infinite trees
data ITree a = ITree a (ITree a) (ITree a)
Another example of <$
usage:
Suppose you have a parser functor P
, and parser :: P A
.
f <$> parser
means that you need to parse something and then apply f
to the result.
a <$ parser
means that you don't need to parse anything (you're not interested in the result) — you only need to recognize, which can be much faster.
See e.g. the regex-applicative library (pay attention to the usage of the Void
constructor).
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