我正在学习Haskell并开始注意以下函数中的常见后缀:
debugM
mapM_
mapCE
其中称为匈牙利表示法。但与此同时,我可以使用类型类来编写非模糊代码,如:
show
return
由于像map
这样的函数如此常见并且在很多上下文中使用,为什么不让类型检查器选择正确的多态版本map
,fmap
,mapM
,{ {1}}或mapM_
?
答案 0 :(得分:25)
有一些"匈牙利符号",但它有很大的不同。简而言之,Haskell的类型系统消除了对大部分系统的需求。
map
/ mapM
是一个很好的例子。这两个函数赋予完全相同的概念,但不能多态表示,因为对差异的抽象会非常嘈杂。所以我们选择匈牙利表示法。
要清楚,这两种类型是
map :: (a -> b) -> ([a] -> [b])
mapM :: Monad m => (a -> m b) -> ([a] -> m [b])
这些看似相似,所有mapM
都是添加monad,但不一样。当您创建以下同义词时,将显示该结构
type Arr a b = a -> b
type Klei m a b = a -> m b
并将类型重写为
map :: Arr a b -> Arr [a] [b]
mapM :: Monad m => Klei m a b -> Klei m [a] [b]
需要注意的是,Arr
和Monad m => Klei m
通常是极其相似的事物。它们都形成了一种称为“类别”的特定结构。这允许我们在其中提升各种计算。 [0]
我们想要的是用
之类的东西来抽象类别的选择class Mapping cat where
map :: cat a b -> cat [a] [b]
instance Mapping (->) where map = Prelude.map
instance Monad m => Mapping (Klei m) where map = mapM -- in spirit anyway
但事实证明,通过使用Functor
[1]
class Functor f where
map :: (a -> b) -> (f a -> f b)
instance Functor [] where
map = Prelude.map
instance Functor Maybe where
map Nothing = Nothing
map (Just a) = Just (f a)
所以为了简单起见,我们使用匈牙利表示法来区分类别,而不是将其汇总到Haskell的多态性功能中。
[0]值得注意的是,Klei m
是一个类别意味着m
是一个单子,而且类别法则完全符合monad法则。特别是,这是我记住monad法律的最佳方式。
[1]从技术上讲,Functor
的唯一方法称为fmap
而不是map
,但它可能也许应该只被称为map
。添加了f
,以便map
的类型签名保持简单(专用于列表),因此对初学者来说有点不那么令人生畏了。这是否是正确的决定是一场持续至今的辩论。
答案 1 :(得分:8)
你的假设是所有这些都大致相同 - 他们没有。 map
和fmap
几乎是相同的功能 - map
仅fmap
专门用于[]
仿函数(由于历史原因,或者初学者会得到更少混淆的类型错误 - 我不确定。)
mapM
和mapM_
分别为map
,后跟sequence
或sequence_
- 而他们正在做的事情看起来可能相关,他们正在做不同的事情。顺便说一下,对于monad来说,行为类似于fmap
的函数是fmap
(由于历史原因,它也会使用liftM
的专用签名别名),Monad
根据定义,s也是Functor
s;请注意,现在这不是标准库所强制执行的 - 如果我没有弄错的话,应该用GHC 7.10纠正历史疏忽。
我不知道该告诉你关于debugM
和mapCE
的内容,因为我之前没有看过这些内容。