Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of (<$) in the Functor class?

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?

like image 313
sdcvvc Avatar asked Dec 30 '12 02:12

sdcvvc


Video Answer


2 Answers

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)
like image 63
Philip JF Avatar answered Sep 19 '22 13:09

Philip JF


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).

like image 29
Roman Cheplyaka Avatar answered Sep 19 '22 13:09

Roman Cheplyaka