当有fmap的时候,Haskell的地图有什么意义呢?
到处都试过用map
, fmap
也起作用了。 为什么Haskell的创build者觉得需要一个map
函数? 难道这不就是目前所知的fmap
, fmap
可以从语言中删除吗?
我想作出回应,提请注意上面的8月份的评论:
实际上并不是这种情况。 发生了什么事情是,地图的types被推广到Haskell 1.3中的Functor。 也就是说,在Haskell 1.3 fmap被称为map。 然后在Haskell 1.4中恢复了这个变化,引入了fmap。 这种变化的原因是教学法; 在向初学者教授Haskell时,一般的地图types使错误信息更难以理解。 我认为这不是解决问题的正确方法。
Haskell 98被一些Haskellers(包括我)看作是一个倒退,以前的版本已经定义了一个更抽象和更一致的库。 好吧。
从Functor
文档引用https://wiki.haskell.org/Typeclassopedia#Functor
你可能会问为什么我们需要一个单独的
map
function。 为什么不直接抛弃当前仅用于列表的map
函数,而是将fmap
重命名为map
呢? 那么,这是一个很好的问题。 通常的观点是,有人刚刚学习Haskell时,错误地使用map
,而不是关于Functor
看到关于列表的错误。
他们在应用程序网站上看起来是一样的,但是当然它们是不同的。 当你将这两个函数( map
或fmap
)应用到值列表中时,它们会产生相同的结果,但这并不意味着它们是为了相同的目的。
运行GHCI会话(格拉斯哥Haskell编译器交互)来查询有关这两个函数的信息,然后查看它们的实现,你会发现许多不同之处。
地图
查询GHCI以获取有关map
信息
Prelude> :info map map :: (a -> b) -> [a] -> [b] -- Defined in 'GHC.Base'
你会看到它被定义为一个高阶函数,适用于任何types的值列表,产生任何types的值列表b
。 虽然多态(上述定义中的a
和b
代表任何types),但是map
函数是用来应用于Haskell中许多其他数据types的值列表 。 map
函数不能应用于不是值列表的东西。
正如您可以从GHC.Base源代码读取的那样 , map
函数的实现如下
map _ [] = [] map f (x:xs) = fx : map f xs
它使用模式匹配将头部( x
)从列表的尾部( xs
)中拉出来,然后使用:
(cons)值构造函数构造一个新列表,以便将fx
前缀(读作“f应用到x“ )到尾部map
的recursion,直到列表为空。 值得注意的是map
函数的实现不依赖于任何其他函数,而仅仅依赖于它本身。
FMAP
现在尝试查询有关fmap
信息,您将看到完全不同的内容。
Prelude> :info fmap class Functor (f :: * -> *) where fmap :: (a -> b) -> fa -> fb ... -- Defined in 'GHC.Base'
这一次, fmap
被定义为其实现必须由那些希望属于Functor
types类的数据types提供的函数之一。 这意味着可以有多个数据types,不仅仅是“值列表”数据types,能够为fmap
函数提供一个实现。 这使得fmap
适用于更大的一组数据types: fmap
!
正如您可以从GHC.Base源代码读取的那样 , fmap
函数的一个可能的实现是由Maybe
数据types提供的:
instance Functor Maybe where fmap _ Nothing = Nothing fmap f (Just a) = Just (fa)
另一种可能的实现是2元组数据types提供的实现
instance Functor ((,) a) where fmap f (x,y) = (x, fy)
而另一个可能的实现是由列表数据types(当然!)提供的实现:
instance Functor [] where fmap f xs = map f xs
它依赖于map
函数(注意那里没有点符号…但是这不在你原来的问题范围之内)。
结论
map
函数可以应用于任何types的值列表(其中值是任何types),而fmap
函数可以应用更多的数据types:属于函子类的所有属性(例如,maybes,元组,列表,等等。)。 由于“值列表”数据types也是一个函子(因为它为它提供了一个实现),所以fmap
也可以应用于生成与map
完全相同的结果。
map (+3) [1..5] fmap (+3) (Just 15) fmap (+3) (5, 7)