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?
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?
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.
Functor
class is too common to ignore, and beginners are often confused by error messages anyway! –
Inequity 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? –
Trochaic liftM
whilst in a do-block rather than fmap
because it fits in better with when I use liftM2
, etc. as well. –
Seamanship liftM
is also useful to implement fmap
in your Functor instance (see also Control.Applicative.liftA
and Data.Traversable.fmapDefault
) –
Bullroarer --fwarn-amp
to help update existing code for the transition. –
Vinni © 2022 - 2024 — McMap. All rights reserved.
map
andliftM
should most certainly do exactly the same thing asfmap
. – Inequityfmap
andliftM
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, whereasmap id getLine
is not. – Doorstone