在 Haskell 98 之前,有 Haskell 1.0 到 1.4。看到这些年来的发展非常有趣,因为功能被添加到标准化 Haskell 的最早版本中。
例如,do-notation 首先由Haskell 1.3标准化(发布于 1996-05-01)。在 中Prelude
,我们找到以下定义(第 87 页):
-- Monadic classes
class Functor f where
map :: (a -> b) -> f a -> f b
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
m >> k = m >>= \_ -> k
class (Monad m) => MonadZero m where
zero :: m a
class (MonadZero m) => MonadPlus m where
(++) :: m a -> m a -> m a
在 Haskell 1.4 中可以找到相同的定义。我对此确实有一些问题(例如,这里还没有发生MonadPlus
改革),但总的来说,这是一个非常好的定义。
这与 Haskell 98 非常不同,Haskell 98 有以下定义:
-- Monadic classes
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
-- Minimal complete definition:
-- (>>=), return
m >> k = m >>= \_ -> k
fail s = error s
这也是 Haskell 2010 中的定义。我对这个定义有以下问题:
MonadZero
并且MonadPlus
消失了。它们是有用的课程。如果 do-notation 中的模式匹配失败...
- Haskell 1.3 使用
zero
. 左零定律适用 (zero >>= k = zero
),所以你知道应该发生什么。 - Haskell 98 使用
fail msg
,如果msg
是 GHC,编译器生成的位置。任何事情都可能发生,不能保证它的语义。因此,它对用户来说并不是什么功能。因此,Haskell 98 的 do-notation 中模式匹配失败的行为是不可预测的!
- Haskell 1.3 使用
名称不太通用(例如
map
vs.fmap
)。不是什么大问题,但它是我眼中的一根刺。
总而言之,我认为这些变化并不是最好的。事实上,我认为它们比 Haskell 1.4 倒退了一步。为什么 Haskell 98 改变了这些东西,为什么会这样?
顺便说一句,我可以想象以下防御:
- “
fail
允许定位错误。” 仅适用于程序员,并且仅在运行时。(不可移植!)错误消息并不完全是您要解析的内容。如果你真的关心它,你应该明确地跟踪它。我们现在Control.Failure
从failure
包中获得,它在这方面做得更好(failure x
表现得大多像zero
)。 - “有太多的类使开发和使用变得太难了。” 类太少违反了它们的规律,而这些规律与类型一样重要。
- “实例限制功能更容易学习。” 那么为什么没有一个
SimplePrelude
替代,大部分类都被删除了呢?对于学生来说,这只是一个神奇的宣言,他们可以做到这一点。(也许{-# LANGUAGE RebindableSyntax #-}
也需要,但同样,学生们非常擅长复制粘贴。) - “实例限制函数使错误更具可读性。” 我使用
fmap
的频率比map
map
,那为什么不listMap
呢?