显然,在数据声明[src],[src]上放置类型类约束是一个坏主意。
我个人并没有遇到过将类型限制在我创建的数据类型中的愿望,但对我来说,为什么语言设计者“决定允许这是一个坏主意”对我来说并不明显。这是为什么?
显然,在数据声明[src],[src]上放置类型类约束是一个坏主意。
我个人并没有遇到过将类型限制在我创建的数据类型中的愿望,但对我来说,为什么语言设计者“决定允许这是一个坏主意”对我来说并不明显。这是为什么?
我个人并没有遇到过将类型限制在我创建的数据类型中的愿望,但对我来说,为什么语言设计者“决定允许这是一个坏主意”对我来说并不明显。这是为什么?
因为它具有误导性,并且与实际有用的东西完全相反。
特别是,它实际上并没有按照您可能期望的方式限制数据类型中的类型。它所做的是对数据构造函数本身设置类约束,这意味着在构造值时需要满足实例......但仅此而已。
因此,例如,您不能简单地定义具有Ord
约束的二叉搜索树,然后知道任何树都有可排序的元素;查找和插入函数本身仍然需要一个Ord
约束。您所要阻止的只是构建一个“包含”某些无序类型的值的空树。就模式匹配而言,对包含的类型完全没有约束。
另一方面,Haskell 的工作人员并不认为明智的版本(人们倾向于假设提供的数据类型上下文)是一个坏主意!事实上,对使用 GADT 语法声明的数据类型(广义代数数据类型,在 GHC 中使用GADTs
语言 pragma 启用)的类约束确实以明显的方式工作——你需要一个约束来构造值,并且有问题的实例也存储在 GADT 中,因此您不需要约束来处理值,并且 GADT 构造函数上的模式匹配允许您使用它捕获的实例。
在数据类型上添加类型类约束实际上并不是一个坏主意 - 它非常有用,并且不会破坏您的其他代码。
坏处在于人们经常期望他们可以使用数据类型来原谅他们对使用数据类型的函数施加约束,但事实并非如此。(您可能会争辩说隐式约束可能会导致问题。)
对数据类型施加约束实际上是将其置于提及受约束类型的所有构造函数上。就像带有约束的普通函数一样,如果使用构造函数,则必须添加约束。我认为这是健康和光明正大的。
它确实确保您不能将数据放入您的数据类型,除非您可以用它做某些事情。它很有用。你不会通过使用一个编程失礼来创建一个编程失礼,这不是一个坏习惯,它不像他们想要的那么可爱。
“允许的坏主意”可能是因为 GADT 确实是他们想要的。
如果 GADT 最早出现,他们就不会这样做。
我认为两者兼而有之并不是一件坏事。如果你想要一个状态操作函数,你可以使用一个永久显式传递的参数,或者你可以使用一个 monad 并使其隐式。如果您想要对数据进行约束,您可以在数据声明中使用永久显式约束,或者在 GADT 中使用隐式约束。GADT 和 monad 更复杂,但它不会使显式参数或数据类型约束出错。