实例的约束推断

时间:2014-11-09 16:35:58

标签: haskell ghc

请考虑以下事项:

{-# LANGUAGE FlexibleContexts #-}
module Foo where

data D a = D a
class Foo b

instance (Num a) => Foo (D a)

f :: (Foo (D a)) => a -> a
f x = x+1

GHC抱怨它无法在Num a中推断f。我希望从Foo D a的(非重叠)实例推断出这个约束。

我知道我可以D使用GADT并在那里添加约束Num a,但我希望不必污染{的构造函数{1}}有很多不必要的约束。这种情况有没有希望发生,现在有可能吗?

2 个答案:

答案 0 :(得分:7)

我猜这会破坏重叠的实例,因此一般不推断。也就是说,你可以拥有

{-# LANGUAGE OverlappingInstances #-}
...
instance (Num a) => Foo (D a)

instance Foo (D Bool)

然后你想要的推断肯定是健全的。

编辑:仔细查看the documentation,可以有

{-# LANGUAGE FlexibleContexts #-}
module Foo where

data D a = D a
class Foo b

instance (Num a) => Foo (D a)

f :: (Foo (D a)) => a -> a
f x = x+1

然后在单独的文件中:

{-# LANGUAGE OverlappingInstances #-}
module Bar where

import Foo

instance Foo Bool

test = f True

也就是说,文档只暗示一个定义两个实例的模块需要有OverlappingInstances标志,所以如果Foo.f可以这样定义,你可以使另一个模块Bar完全打破类型安全。请注意,使用GHC的单独编译,f将在不知道模块Bar的情况下完全编译。

答案 1 :(得分:5)

箭头=>方向性。这意味着如果 Num a拥有Foo (D a)。这并不意味着如果Foo (D a)成立,则Num a成立。

Foo (D a)存在(并且永远不会)任何重叠实例的知识应该意味着反向意义也是如此,但(a)GHC不知道这一点和(b)GHC& #39; s实例机器未设置为使用此知识。

要实际编译使用类型类的函数,GHC仅仅证明类型必须是类的实例是不够的。它必须实际提出一个特定实例声明,它提供了成员函数的定义。我们需要一个建设性的证据,而不仅仅是存在证明。

要识别C类的实例,它可以重用一个将由正在编译的函数的调用者选择的实例,或者它必须具体知道所涉及的类型以选择单个实例从那些可用。正在编译的函数只有在C具有约束条件时才会传递给C的实例。否则该函数必须是足够单形的,它只能使用单个实例。

特别考虑你的例子,我们可以看到f对Foo (D a)有一个约束,所以我们可以依赖为我们提供的调用者。但来电者不会给我们Num a的实例。即使你假设我们从Num a上的Foo (D a)约束知道某个地方肯定有这样的实例,我们也不知道a是什么,所以{+的定义是什么1}}我们应该调用吗?我们甚至无法调用另一个函数,该函数适用于任何Num a但在类外定义,因为它们都将具有Num a约束,因此我们希望为他们确定一个实例。知道一个没有拥有实例的实例是没用的。

一点也不明显,但您实际上要求GHC做的是对运行时到达的类型a进行运行时切换。这是不可能的,因为我们应该发布适用于Num中任何类型的代码,甚至尚未存在的类型,或者其实例尚不存在。< / p>

工作的类似想法是当您对而不是实例有约束时。例如:

class Num a => Foo a 

f :: Foo a => a -> a
f x = x + 1

但这只有效,因为我们知道所有 Foo个实例必须有一个对应的Num实例,因此Foo a中所有函数的调用者都是多态的知道也选择一个Num实例。因此,即使不知道特定的a以便选择Num个实例,f也知道其调用方还会提供Num个实例以及Foo个实例实例

在您的情况下, FooNum一无所知。在其他示例中,Num甚至可能未在定义类Foo的模块可访问的代码中定义。它是,用于设置必须提供的所需信息,以调用类型类中的多态函数,并且这些多态函数必须能够在没有任何特定知识的情况下工作某个例子。

因此Num a => Foo (D a)实例无法存储Num实例 - 事实上,实例定义在a中也是多态的,所以&# 39;即使有空间也无法选择要存储的特定实例!因此,即使f可能知道 来自Num a的{​​{1}}实例(如果我们假定某些知识不会涉及任何重叠) ,它仍然需要一个Foo (D a)约束,以便要求其调用者选择一个Num a实例供其使用。