2012-02-02 45 views
7

这是一个哲学问题,但我希望能够通过官方文档或“上帝的话”来回答(阅读:SPJ)。 Haskell委员会选择以类型类型的形式需要显式接口而不是基于模式匹配的更统一的解决方案,是否有特定的原因?为什么类型类而不是模式匹配?

举个例子,拿Eq

class Eq a where 
    (==), (/=) :: a -> a -> Bool 
    x == y = not $ x /= y 
    x /= y = not $ x == y 

instance Eq Int where 
    (==) = internalIntEq 

为什么我们不能做这样的事情,而不是(承担与伪哈斯克尔):

(==), (/=) :: a -> a -> Bool 
default x == y = not $ x /= y    -- 1 
default x /= y = not $ x == y 

(Int a) == (Int b) = a `internalIntEq` b -- 2 

也就是说,如果哈斯克尔是到允许普通数据类型的模式匹配,则:

  • 程序员可以创建特设班,即instance是隐式的(2)

  • 类型仍然可以推断和静态匹配(SupportsEqualsEquals a => ...

  • 默认实现会来“免费”

  • 类可以随时可以扩展而不会破坏任何东西

需要一种方法来指定默认模式(1),尽管在其他s,总是匹配最后一个。这些假设的功能是否与Haskell内在的特性相冲突?正确推断类型会变得困难还是不可能?这看起来像是一个强大的功能,与Haskell的其他部分相得益彰,所以我认为这是一个很好的理由,我们不要这样做。这种ad-hoc多态性机制简直就是也是 ad-hoc?

+0

我不是很精通,但我认为一个主要问题是,你失去parametricity,从而自由定理:你知道什么是'ID :: A - >了'或'FST ::(一, b) - > a'仅仅通过它们的类型签名来完成,因为它们只有参数多态性。如果你有某种类型的机制,那么你失去了参数。例如,'a - > a - > Bool'只允许Haskell中的两个(全部)实现('const $ const True'和'const $ const False');如果你有typecase,那么你失去了这个保证。 – 2012-02-02 04:04:32

+0

@ AntalS-Z:只要确保我理解正确。你是说我可以声明'id :: a - > a',然后定义id(Int i)= i + 1'?因为情况并非如此:后一种定义将是'SupportsPlus a => a - > a',这是不兼容的。 – 2012-02-02 04:11:01

+5

这就是你的问题听起来的样子;您的'(==)'的类型签名以'a - > a - > Bool'的形式给出。如果你必须指定一个“type class”'SupportsEquals',那么看起来你所做的一切都是交易语法---我不明白为什么它*不同*。我不认为它可以表达多参数类型类,函数依赖关系或关联类型,尽管它们当然不在原始计划中。事实上,它甚至可以表达更高级的类型类吗?你怎么知道'return :: a - > m a'应该是'm'上的ad-hoc,而不是'a'? – 2012-02-02 04:22:26

回答

12

ad-hoc多态性的这种机制是不是太专门?

这个问题简直要与Philip Wadler和Steve Blott于1988年发表的文章How to make ad-hoc polymorphism less ad-hoc联系在一起,他们提出了Type类的概念。 Wadler可能是“上帝的话语”。

我在提出的“任何Haskell数据类型的模式匹配”技术中遇到了一些问题。

模式匹配技术是不足以定义多态常量,如mempty :: Monoid a => a

模式匹配技术仍然回落到类型类中,除非更糟。类型类分类类型(去图)。但是模式匹配技术使得它相当模糊。您应该如何指定功能foobar是“相同”类的一部分? 如果您必须为您使用的每个单一多态函数添加一个新类型,那么类型类约束将变得完全不可读

模式匹配技术向Haskell引入了新的语法,使语言规范复杂化。 default关键字看起来不怎么糟糕,但模​​式匹配“on types”是新的和令人困惑的。

模式匹配“在普通数据类型”上击败无点风格。而不是(==) = intEq,我们有(Int a) == (Int b) = intEq a b;这种人造模式匹配防止eta减少

最后,它完全改变了我们对类型签名的理解。a -> a -> Foo目前是保证输入不能被检查。对于a输入,除了两个输入是相同类型外,没有什么可以假设的。[a] -> [a]又意味着列表的元素不能以任何有意义的方式进行检查,给你Theorems for Free(另一Wadler纸)。

有可能是解决这些问题的方法,但是我总的印象是类型类已经在一个优雅的方式解决这个问题,建议的模式匹配技术不会增加效益,同时引起一些问题。

+1

wrt。你的最后一点,我不认为他的建议是完全改变多态性,我认为这只是为每个使用的函数添加一堆类型约束。即'a - > a - > Foo'仍然保证输入不能被检查 - 如果使用隐式类型类型,它将成为签名的一部分。 – gatoatigrado 2012-02-02 10:26:58

+0

非常感谢您的见解和论文。不过,我不同意你的观点。类型类约束不会变得不可读,因为它们甚至不需要可见 - 你可以只说“(+)没有在(foo :: Foo + bar :: Bar)中定义'”。关于类型签名的观点很重要。你知道'id :: a - > a'这个事实,因为涉及任何其他函数的定义将具有不兼容的签名。另外,重新。 eta减少,你写在一个应用程序(而不是拼接)的语言,所以不是所有的东西都可以是无点的。 – 2012-02-02 14:04:56

+0

@ gatoatigrado:是的,这正是我的意思。它不会破坏我们已有的任何定理。至少我不这么认为。 – 2012-02-02 14:05:34

4

我不知道神的话语,但这里有几个参数。

不再是唯一在同一模块中定义的函数。你现在可以写

(==) = internalIntEq 
(==) = internalFloatEq 

这使得代码更不可读。有一个名为“TypeBasedNameResolution”的提议做了类似的事情,但重要的事实是这种类型分支只能从不同模块中为(==)完成。

这是不好的做法,让编译器在标识符增加。在你的情况下,你会自动创建类型类SupportsEqualsEquals。新用户可能会问,“这是从哪里来的”,并且没有相应的来源定义它。

跳过书写实例签名并不会给您带来如您所想的那么多。您可以通过例如ghci中的:t internalIntEq。我想这可能会更方便,但我宁愿有一个工具,我可以问“Eq的实例类型是什么,其中==internalIntEq。”。

更先进的职业特性都不清楚。你在哪里放置相关的类型和函数依赖关系?这些对我来说真的很重要!

您的违约使模块编译更加困难。你不会免费获得可扩展的类。考虑,

f :: Supports[==] a => a -> a -> Bool 
f = (/=) 

据我了解,这个会编译成

f :: Instance (Supports[==]) a -> a -> a -> Bool 
f eq_inst x y = not (x eq_inst.== y) 

现在,如果我提供了一个特定类型a_0/=实例,并喂一些x :: a_0f,然后

f x x = not (x == x) 
-- computation you really want: f x x = x /= x, using the new /= instance for a_0 

你可能会问,“什么时候会这么愚蠢的将f限制为Supports[==]而不是Supports[/=]?“但是,上下文可以来自多个函数签名;他们可以来自更高级的功能等等。

希望它有帮助。