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

后端 未结 3 1935
遇见更好的自我
遇见更好的自我 2020-12-04 11:06

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

3条回答
  •  一个人的身影
    2020-12-04 12:06

    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)
    

提交回复
热议问题