2016-08-30 93 views
2

我目前正在一个项目中为一个类派生一些实例。由于类只有一个方法,除了一些特定的情况外,它们将具有相同的定义,所以我尝试定义一个可重叠的一般实例,然后定义我需要重叠的实例。重叠实例的问题

这不起作用,因为我得到重叠的实例错误。做一些测试,我们来到翻过这个减少的例子,这相当于我原来的问题相当多:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, MultiParamTypeClasses #-} 

module Instance where 

data Id a = Id a String 

data C a = C a 

class Bad a b where 
    bad :: a -> String 

instance {-# OVERLAPPABLE #-} Bad a b where 
    bad = \_ -> "Default case" 

instance {-# OVERLAPPING #-} Bad (Id a) (C a) where 
    bad = \_ -> "Id" 

class Good a b where 
    good :: a -> String 

instance {-# OVERLAPPABLE #-} Good a b where 
    good = \_ -> "Default case" 

instance {-# OVERLAPPING #-} Good (Id a) b where 
    good = \_ -> "Id" 

test = let a = Id() "a" 
     in putStrLn (good a) >> putStrLn (bad a) 

(需要注意的是,除非你对此有何评论第二个坏情况下这不会编译)

类好的作品没有任何问题(测试输出“Id”)。如果我不删除第二个实例坏了,我得到:

Overlapping instances for Bad (Id()) b0 
    arising from a use of ‘bad’ 
    Matching instances: 
    instance [overlappable] Bad a b -- Defined at Instance.hs:12:31 
    instance [overlapping] Bad (Id a) (C a) 
     -- Defined at Instance.hs:15:30 
    (The choice depends on the instantiation of ‘b0’ 
    To pick the first instance above, use IncoherentInstances 
    when compiling the other instance declarations) 
    In the first argument of ‘putStrLn’, namely ‘(bad a)’ 
    In the second argument of ‘(>>)’, namely ‘putStrLn (bad a)’ 
    In the expression: putStrLn (good a) >> putStrLn (bad a) 

我不明白的是为什么会发生这种情况,当他们之间的唯一区别是在第二类参数的aditional的限制。

另外,是不是可重叠实例的重点,以避免重叠错误?

问候

+0

这些类不会真正起作用 - 类中的函数没有提到变量'b',并且没有fundep或类似的从'a'确定'b'。 “好”只能在非常巧合的情况下才起作用 - 在“好”这两个例子中,第二种类型是完全不受限制的类型变量。这基本上与你没有类中的类型变量相同。 'bad'不起作用,因为'b'是一个完全自由的类型变量,它可能会在稍后的时间被实例化为'C a',这将改变选择哪个实例(“选择取决于实例化”)。 – user2407038

+0

我认为你的编译指示应该有'AllowAmbiguousTypes'而不是'UndecidableInstances',否则你会得到一个不同的错误,这个错误与函数签名'bad :: Bad ab => a - > String'中的'b'有关系... – Alec

+0

@ user2407038是的,我可以看到,尽管如果将b添加到函数中,错误仍然存​​在。再测试一下,我看到你基本上必须使用这个函数,而明确地说你的b是什么,这样ghc知道使用哪个实例。我可以得到这个小例子的工作,但我认为这在更大的项目中是不可行的。谢谢! –

回答

3

按照我上面的评论,我觉得你的编译指示应该有AllowAmbiguousTypes而不是UndecidableInstances, 否则你会得到一个不同的错误(至少我是这样的GHC 8.0.1)有关b是暧昧功能签名 bad :: Bad a b => a -> String

AmbiguousTypes允许您为使用时不明确的函数编写签名。 取而代之,歧义检查将移至呼叫地点。这与TypeApplications 之类的东西非常相似,可以指定那些不明确的变量。在这种情况下,bad始终不明确,因此我们需要此编译指示移至呼叫站点的 错误消息。现在,我有和你一样的信息。


,即使OVERLAPPABLEOVERLAPPING哈斯克尔抱怨的原因是,根据b如何实例化(尚未 指定),它会选择Bad两个实例之一。换句话说,你可以bC a统一, 一样好,你不能,所以Haskell中抛出了它的手,说:“你还没有告诉我有足够的了解b我要 能够挑选Bad的最具体实例“。

在另一方面,即使不知道b,哈斯克尔知道哪些实例Good a bGood (Id a) b都比较 具体的 - 它始终是第二个(甚至不知道什么b是这样的话)。

我真的推荐你阅读关于overlapping instances 的文档,因为它解释了整个算法。


您通常可以使用周围事物一样TypeApplications(指定b),或翻译您类型类,一类家庭这些问题得到。