What's the point of map in Haskell, when there is fmap?
Asked Answered
R

3

115

Everywhere I've tried using map, fmap has worked as well. Why did the creators of Haskell feel the need for a map function? Couldn't it just be what is currently known as fmap and fmap could be removed from the language?

Raye answered 26/7, 2011 at 1:17 Comment(3)
I think you are asking 'What's the point of fmap in Haskell'?Fiacre
I know what the point of fmap is. It's to map a function over a Functor instance. I'm wondering abou8t the purpose of the specialization to map.Raye
Similarly what is the point of (.) when there is fmap (tongue in cheek).Ternopol
J
109

I would like to make an answer to draw attention to augustss's comment:

That's not actually how it happens. What happened was that the type of map was generalized to cover Functor in Haskell 1.3. I.e., in Haskell 1.3 fmap was called map. This change was then reverted in Haskell 1.4 and fmap was introduced. The reason for this change was pedagogical; when teaching Haskell to beginners the very general type of map made error messages more difficult to understand. In my opinion this wasn't the right way to solve the problem.

Haskell 98 is seen as a step backwards by some Haskellers (including me), previous versions having defined a more abstract and consistent library. Oh well.

Joslyn answered 26/7, 2011 at 9:12 Comment(5)
Are these backward steps collected and documented anywhere? It would be interesting to see what else was considered a back step and if there are better solutions to them as well.Teenateenage
The map and fmap has been around for a long time - it was reheated on the Haskell-prime mailing list in August 2006 - haskell.org/pipermail/haskell-prime/2006-August/thread.html. As a counterpoint, I prefer the status quo. To me, it seems valuable that there's a subset of Haskell that corresponds roughly to Miranda. In the UK, Miranda was used as a teaching language for maths students not just computer science students. If that niche isn't already lost to a non-functional language (e.g. Mathematica) I don't see Haskell with a unified map filling it.Sophi
And I would further like to note, for anyone not already aware, that augustss is Lennart Augustsson, who for all practical purposes has been part of the Haskell community since before Haskell existed, cf. A History of Haskell, so the comment in question is not in any way second-hand hearsay!Youngman
There is now Nitpicks page on Haskell wiki where this issue is mentioned.Ampereturn
It's funny that this decision was made for pedagogical reasons, because I confused fmap with flatmap all the time when learning Haskell. They should have gotten a n00b focusgroup together. :)Colson
F
37

Quoting from the Functor documentation at https://wiki.haskell.org/Typeclassopedia#Functor

You might ask why we need a separate map function. Why not just do away with the current list-only map function, and rename fmap to map instead? Well, that’s a good question. The usual argument is that someone just learning Haskell, when using map incorrectly, would much rather see an error about lists than about Functor.

Franklynfrankness answered 13/10, 2015 at 11:27 Comment(2)
This makes a lot of sense to me.Christabelle
So this should've been a GHC hint, not a new function...Colous
P
35

They look the same on the application site but they're different, of course. When you apply either of those two functions, map or fmap, to a list of values they will produce the same result but that doesn't mean they're meant for the same purpose.

Run a GHCI session (the Glasgow Haskell Compiler Interactive) to query for information about those two functions, then have a look at their implementations and you will discover many differences.

map

Query GHCI for information about map

Prelude> :info map
map :: (a -> b) -> [a] -> [b]   -- Defined in ‘GHC.Base’

and you'll see it defined as an high-order function applicable to a list of values of any type a yielding a list of values of any type b. Although polymorphic (the a and b in the above definition stand for any type) the map function is intended to be applied to a list of values which is just one possible data type amongst many others in Haskell. The map function could not be applied to something which is not a list of values.

As you can read from the GHC.Base source code, the map function is implemented as follows

map _ []     = []
map f (x:xs) = f x : map f xs

which makes use of pattern matching to pull the head (the x) off the tail (the xs) of the list, then constructs a new list by using the : (cons) value constructor so to prepend f x (read it as "f applied to x") to the recursion of map over the tail until the list is empty. It's worth noticing that the implementation of the mapfunction does not rely upon any other function but just on itself.

fmap

Now try to query for information about fmap and you'll see something quite different.

Prelude> :info fmap
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  ...
  -- Defined in ‘GHC.Base’

This time fmap is defined as one of the functions whose implementations must be provided by those data types which wish to belong to the Functor type class. That means that there can be more than one data types, not only the "list of values" data type, able to provide an implementation for the fmap function. That makes fmap applicable to a much larger set of data types: the functors indeed!

As you can read from the GHC.Base source code, a possible implementation of the fmap function is the one provided by the Maybe data type:

instance  Functor Maybe  where
  fmap _ Nothing       = Nothing
  fmap f (Just a)      = Just (f a)

and another possible implementation is the one provided by the 2-tuple data type

instance Functor ((,) a) where
  fmap f (x,y) = (x, f y)

and another possible implementation is the one provided by the list data type (of course!):

instance  Functor []  where
  fmap f xs = map f xs

which relies upon the map function.

Conclusion

The map function can be applied to nothing more than list of values (where values are of any type) whereas the fmap function can be applied much more data types: all of those which belongs to the functor class (e.g. maybes, tuples, lists, etc.). Since the "list of values" data type is also a functor (because it provides an implementation for it) then fmap can be applied to is as well producing the very same result as map.

map  (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)
Prevalent answered 24/7, 2017 at 20:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.