我目前正在一个项目中为一个类派生一些实例。由于类只有一个方法,除了一些特定的情况外,它们将具有相同的定义,所以我尝试定义一个可重叠的一般实例,然后定义我需要重叠的实例。重叠实例的问题
这不起作用,因为我得到重叠的实例错误。做一些测试,我们来到翻过这个减少的例子,这相当于我原来的问题相当多:
{-# 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的限制。
另外,是不是可重叠实例的重点,以避免重叠错误?
问候
这些类不会真正起作用 - 类中的函数没有提到变量'b',并且没有fundep或类似的从'a'确定'b'。 “好”只能在非常巧合的情况下才起作用 - 在“好”这两个例子中,第二种类型是完全不受限制的类型变量。这基本上与你没有类中的类型变量相同。 'bad'不起作用,因为'b'是一个完全自由的类型变量,它可能会在稍后的时间被实例化为'C a',这将改变选择哪个实例(“选择取决于实例化”)。 – user2407038
我认为你的编译指示应该有'AllowAmbiguousTypes'而不是'UndecidableInstances',否则你会得到一个不同的错误,这个错误与函数签名'bad :: Bad ab => a - > String'中的'b'有关系... – Alec
@ user2407038是的,我可以看到,尽管如果将b添加到函数中,错误仍然存在。再测试一下,我看到你基本上必须使用这个函数,而明确地说你的b是什么,这样ghc知道使用哪个实例。我可以得到这个小例子的工作,但我认为这在更大的项目中是不可行的。谢谢! –