107
map :: (a -> b) -> [a] -> [b]

fmap :: Functor f => (a -> b) -> f a -> f b

liftM :: Monad m => (a -> b) -> m a -> m b

Why do we have three different functions that do essentially the same thing?

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 33
    History, mostly. fmap is distinct from map for pedagogical reasons, liftM distinct from fmap for historical reasons (namely Functor not being a superclass of Monad) – luqui Sep 18 '11 at 18:37
  • 12
    Oh, and just for the sake of being clear: They don't "essentially" do the same thing. Both `map` and `liftM` should most certainly do *exactly* the same thing as `fmap`. – C. A. McCann Sep 18 '11 at 19:33
  • 3
    While `fmap` and `liftM` do exactly the same thing, `map` of course is only a special case of them, i.e. something different. `fmap id getLine` is well-typed, whereas `map id getLine` is not. – Thorsten Dec 04 '17 at 17:48

1 Answers1

96

map exists to simplify operations on lists and for historical reasons (see What's the point of map in Haskell, when there is fmap?).

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

-- Typeclassopedia, page 20

fmap and liftM exist because monads were not automatically functors in Haskell:

The fact that we have both fmap and liftM is an unfortunate consequence of the fact that the Monad type class does not require a Functor instance, even though mathematically speaking, every monad is a functor. However, fmap and liftM are essentially interchangeable, since it is a bug (in a social rather than technical sense) for any type to be an instance of Monad without also being an instance of Functor.

-- Typeclassopedia, page 33

Edit: agustuss's history of map and fmap:

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.

-- What's the point of map in Haskell, when there is fmap?

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
li.davidm
  • 11,736
  • 4
  • 29
  • 31
  • 14
    And, from my perspective as someone who first encountered Haskell more than a decade after the change @augustss describes was made, and has spent a lot of time helping people who are learning the language now, it's not at all clear that it even helped in any way. Certainly not enough to offset the useless redundancy (which itself leads to people asking questions like this one); the `Functor` class is too common to ignore, and beginners are often confused by error messages anyway! – C. A. McCann Sep 18 '11 at 19:30
  • 14
    Can't we just remove `liftM`? Let code break, who cares, it usually takes less than 2 days for code to be fixed on github and then uploaded on hackage. Or am I being wild and crazy? – Tarrasch Sep 18 '11 at 21:13
  • 3
    @Tarrasch: not everyone uses github, not all packages have a great track-record for being updated on time, and I for one tend to use `liftM` whilst in a do-block rather than `fmap` because it fits in better with when I use `liftM2`, etc. as well. – ivanm Sep 18 '11 at 22:39
  • `liftM` is also useful to implement `fmap` in your Functor instance (see also `Control.Applicative.liftA` and `Data.Traversable.fmapDefault`) – Ben Millwood Sep 29 '11 at 03:05
  • 1
    @L01man people have worked on this; see http://stackoverflow.com/questions/5730270/alternative-implementations-of-haskells-standard-library-type-classes and at least for numeric classes, there are alternatives: http://hackage.haskell.org/packages/archive/numeric-prelude/0.3.0.2/doc/html/NumericPrelude.html – li.davidm Jun 09 '12 at 19:24
  • 1
    @L01man Yes this will soon be fixed. The [Applicative Monad Proposal](http://www.haskell.org/haskellwiki/Functor-Applicative-Monad_Proposal) (AMP) looks like it will pass into the next version of Haskell. [GHC 7.8.3](http://www.haskell.org/ghc/docs/latest/users_guide.pdf) has a new flag `--fwarn-amp` to help update existing code for the transition. – recursion.ninja Aug 22 '14 at 15:38
  • So can you use fmap where you would use liftM and use liftM where you would use fmap? Are they completely interchangeable? – CMCDragonkai Sep 15 '14 at 11:59
  • @CMCDragonkai see McCann's comment on the original question above. They are exactly the same. – li.davidm Sep 15 '14 at 12:52