为什么Haskell 98的标准类不如Haskell 1.3? [英] Why were Haskell 98's standard classes made inferior to Haskell 1.3's?
问题描述
Prelude
中,我们找到了下列定义(第87页): - 一元类
类Functor f其中
map ::(a - > b) - > f a - > f b
class Monad m其中
(>> =):: 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其中
zero :: m a
class(MonadZero m)=> MonadPlus m其中
(++):: m a - > m a - > m
Haskell 1.4中提供了相同的定义。我确实遇到了一些问题(例如 MonadPlus
改革在这里还没有发生),但总体而言,这是一个非常好的定义。
这与Haskell 98非常不同,后者的定义如下找到:
- Monadic类
类Functor f其中
fmap ::(a - > b) - > f a - > f b
class Monad m其中
(>> =):: m a - > (a - > m b) - > m b
(>>):: m a - > m b - > m b
return :: a - > m a
fail :: String - > m a
- 最小完整定义:
- (>> =),返回
m>> k = m>> = \\ __ - > k
失败s =错误s
这也是Haskell 2010中的定义。这个定义有以下问题:
-
MonadZero
和MonadPlus
不见了。它们是有用的类。 -
如果某个符号中的模式匹配失败...
- Haskell 1.3使用
zero
。左零法则适用(零>> = k =零
),所以你知道应该发生什么。
- Haskell 98使用
fail msg
,其中msg
是GHC编译器生成的。任何事情都可能发生,并不保证其语义。因此,这对用户来说不是什么功能。结果,Haskell 98的do-notation模式匹配失败的行为是不可预知的! 名称不太一般(例如 - Haskell 98使用
map
与fmap
)。不是一个大问题,但它是我眼中的刺。 >总而言之,我认为这些变化并不是最好的。事实上,我认为他们是从Haskell 1.4退步的。 为什么Haskell 98的这些改变,以及为什么这样呢? - Haskell 1.3使用
-
失败
允许用于查找错误。仅适用于程序员,并且仅限于运行时。 (不可移植的!)错误信息不完全是你想要解析的东西。如果你真的关心它,你应该明确地跟踪它。我们现在从failure
包中有Control.Failure
,它在这方面做得更好(失败x
的行为大部分类似于零
)。 开发和使用太难了。课程太少会破坏他们的法律,而这些法律与类型一样重要。 实例限制的函数更容易学习。然而,为什么没有SimplePrelude
,而大多数类都被删除了?这只是一个魔法宣言,对于学生来说,他们可以管理那么多。 (也许还需要{ - #LANGUAGE RebindableSyntax# - }
,但同样,学生们在复制粘贴的时候非常好。) li>
- 实例限制函数使错误更具可读性。我比<$ c更经常使用
fmap
$ c> map ,那么为什么不map 和 listMap
改为
为什么这些事情为了Haskell 98而改变,为什么以这种方式?
Haskell 98对语言进行了大量简化(其中大部分已经被颠倒过来)。目标是改善Haskell作为教学语言,并作出相对保守的选择。
见例如
我们认为Haskell 98是一个相当保守的设计。以
为例,那时多参数类型类被广泛使用
,但Haskell 98只有单参数类型类(Peyton
Jones et al。,1997)。 p>
在:历史的哈斯克尔
和:
Haskell 98绝不会是Haskell的最后一个版本。在
上相反,我们设计它知道新语言扩展
(多参数类型类,通用和存在
量化,模式守卫等等)正在顺利完成。
但是,Haskell 98将会有一个特殊的状态:意图是
Haskell编译器将继续支持Haskell 98(即使在
适当的标志处),即使该语言的后续版本已经为
定义,所以名称`Haskell 98'将引用一个固定的,稳定的
语言。
所以,事情简化了,目标是制定更简单的标准。
Before Haskell 98, there were Haskell 1.0 through 1.4. It's pretty interesting to see the development throughout the years, as features were added to the earliest versions of standardized Haskell.
For instance, the do-notation was first standardized by Haskell 1.3 (published 1996-05-01). In the
Prelude
, we find the following definitions (page 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
The same definitions are found in Haskell 1.4. I do have a few problems with this (e.g. the
MonadPlus
reform hasn't happened here yet), but overall, it is a very nice definition.This is very different from Haskell 98, where the following definition is found:
-- 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
This is also the definition in Haskell 2010. I have the following problems with this definition:
MonadZero
andMonadPlus
are gone. They were useful classes.In case of a pattern match failure in a do-notation...
- Haskell 1.3 uses
zero
. The Left Zero law applies (zero >>= k = zero
), so you know what's supposed to happen. - Haskell 98 uses
fail msg
, wheremsg
is compiler-generated in case of GHC. Anything can happen, no guarantees about its semantics. Therefore, it's not much of a function for users. As a consequence, the behaviour of pattern match failures in Haskell 98's do-notation is unpredictable!
- Haskell 1.3 uses
Names are less general (e.g.
map
vs.fmap
). Not a big problem, but it's a thorn in my eye.
All in all, I think these changes weren't for the best. In fact, I think they were a step backwards from Haskell 1.4. Why were these things changed for Haskell 98, and why in this way?
As an aside, I can imagine the following defenses:
- "
fail
allows for locating errors." Only for programmers, and only at runtime. The (unportable!) error message is not exactly something you want to parse. If you really care about it, you should track it explicitly. We now haveControl.Failure
from thefailure
package, which does a much better job at this (failure x
behaves mostly likezero
). - "Having too many classes makes development and use too hard." Having too few classes breaks their laws, and those laws are just as important as types.
- "Instance-restricted functions are easier to learn." Then why isn't there a
SimplePrelude
instead, with most of the classes removed? It's only one magical declaration away for students, they can manage that much. (Perhaps{-# LANGUAGE RebindableSyntax #-}
is needed too, but again, students are very good at copy-pasting stuff.) - "Instance-restricted functions make errors more readable." I use
fmap
much more often thanmap
, so why notmap
andlistMap
instead?
解决方案Why were these things changed for Haskell 98, and why in this way?
Haskell 98 involves a lot of simplification to the language (much of which has since been reversed). The goal was to improve Haskell as a teaching language, and to make relatively conservative choices.
See e.g.
We regarded Haskell 98 as a reasonably conservative design. For example, by that time multi-parameter type classes were being widely used, but Haskell 98 only has single-parameter type classes (Peyton Jones et al., 1997).
And:
Haskell 98 will by no means be the last revision of Haskell. On the contrary, we design it knowing that new language extensions (multi-parameter type classes, universal and existential quantification, pattern guards, etc, etc) are well on the way. However, Haskell 98 will have a special status: the intention is that Haskell compilers will continue to support Haskell 98 (given an appropriate flag) even after later versions of the language have been defined, and so the name `Haskell 98' will refer to a fixed, stable language.
In: Haskell98 report
So, things were simplified, with the goal of producing a simpler standard.
这篇关于为什么Haskell 98的标准类不如Haskell 1.3?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- 实例限制函数使错误更具可读性。我比<$ c更经常使用
除此之外,我可以想象下面的防御措施: