Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between map of Python and fmap of Haskell?

Tags:

python

haskell

I know that they are both different languages, but then aren't they both essentially the same? fmap applies a function over a Functor, whereas map of Python applies a function over an iterable. So if there is any difference between the two, can you provide an example of something we can do with map of Python but not fmap of Haskell (or vice versa)?

like image 864
Programmer Avatar asked Jul 17 '17 06:07

Programmer


People also ask

What is Fmap in Haskell?

You can think of fmap as either a function that takes a function and a functor and then maps that function over the functor, or you can think of it as a function that takes a function and lifts that function so that it operates on functors. Both views are correct and in Haskell, equivalent.

What is map in Haskell?

map takes a function and a list and applies that function to every element in the list, producing a new list.


2 Answers

They’re pretty different.

The two functions do very similar things on list-like things, but that’s really where the similarities end. It would probably be more useful to compare Haskell’s map with Python’s map, but fmap is both far more general and rather different in meaning.


In Haskell, fmap is a sort of “lifting” or “piercing” operation. It takes a function from a -> b and “lifts” it to a function from f a -> f b. A list-like container is a sort of thing that can be “pierced”, but so many more things than lists are functors!

Mapping over a Maybe a is extremely useful and common, though one might argue that Maybe is just like a list of zero or one elements. However, consider the fact that functions are functors in Haskell. You can fmap over a function and receive a new function, but functions are obviously not iterables. Similarly, parsers in parser combinator libraries like parsec are functors, and you can produce new parsers by fmapping over them to apply a function to their result.

All sorts of things are functors, many of which have little to do with containers that hold values. All monads are functors, including things like Writer, State, and Cont, which are effectful computations, not list-like things. Other things are functors, too, like Const, which can be combined with Lift to form an applicative functor that monoidally collects errors.

The specifics of what each of these things are and do is outside the scope of this question, but the point is that the Haskell notion of a functor is far more general than an “iterable thing”, and it’s generally used in a different way from Python’s map.


From another perspective, however, it’s fair to say that Python’s map is more powerful than Haskell’s fmap. There is a cost to generality, and that’s a need to be simple. Being specific gives you the option to do more things! This, plus Python’s other differences from Haskell (like dynamic typing and variadic functions), makes it possible for Python’s map to do things that Haskell’s fmap cannot.

As Daniel Sanchez points out, Python’s map is variadic, so it acts like zipWith in Haskell when provided multiple iterables:

>>> map(lambda x,y: x+y, range(10), range(10))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

There’s a bit more, though. Python’s map can also operate over different sorts of things. In Haskell, the generality of the Functor class is useful mostly because it makes it possible to write functions that operate on any Functor, and that means all Functors need to act in more or less the same way. The element(s) “inside” a f a, if you want to use the container analogy, must be of type a.

Consider what this means for something like a hash map or dictionary. In Haskell, Functors must have kind * -> *, so Map can only be a Functor if partially applied. Therefore, Map k has a Functor instance, so fmap only operates over the values of a Map.

In Python, this is not nearly so rigidly enforced by the consequences of the type system, so dictionaries actually function as sequences of their keys when iterated over:

>>> map(None, {'foo': 1, 'bar': 2})
['foo', 'bar']

Oh, and that example also demonstrates another Python oddity—mapping None over something acts like mapping the identity function, but mapping it over multiple things acts like Haskell’s zip and produces tuples:

>>> map(None, [1, 2, 3], [4, 5, 6])
[(1, 4), (2, 5), (3, 6)]

Finally, note that Haskell’s fmap cannot map over sets! Why is this? Well, the rigid type system requires that fmap work for any values, but Sets must be composed of values of a type with an Ord instance. Python does not need to jump through any such hoops, so it can map over sets just fine:

>>> map(None, {1, 2, 3})
[1, 2, 3]

Python and Haskell are very different languages. A lot of these differences are simply artifacts of the language differences, not necessarily differences in what the functions are designed to do.

Still, the core difference is simple to summarize: Python’s map is for mapping over list-like things, but Haskell’s fmap is for lifting a function to operate on an arbitrary data structure. Those two things are quite distinct even once you look past language differences, so don’t assume they’re the same just because that have the same sort of shape on the surface.

like image 132
Alexis King Avatar answered Nov 15 '22 06:11

Alexis King


Python map has one behaviour that Haskell does not:

map(lambda x,y: x+y, range(10), range(10))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

What it does its iterate over the pairs of n iterables unpacking the values from them and passing them as arguments to the function. Actually Haskell map can't do that due to its type signature fmap :: Functor f => (a -> b) -> f a -> f b

That python map usage is similar to Haskell zipWith:

Prelude> zipWith (\x y -> x+y) [1..10] [1..10]
[2,4,6,8,10,12,14,16,18,20]
like image 33
Netwave Avatar answered Nov 15 '22 04:11

Netwave