Chr*_*lor 5 haskell types type-extension
我正在尝试编写一些Haskell代码,其中有多种数据类型,每种类型都可以有多个实现.为此,我将每个数据类型定义class为其方法是相关的构造函数和选择器,然后根据给定的构造函数和选择器对该类的成员实现所有操作.
例如,也许A是多项式类(方法getCoefficients和makePolynomial),其可以具有表示作为SparsePoly或DensePoly和B是复数类(方法getReal,getImag并且makeComplex其可被表示为一个)ComplexCartesian或一个ComplexPolar.
我在下面复制了一个最小的例子.我有两个类A,B每个类都有一个实现.我想将这两个类的所有实例都Num自动化为实例(这需要FlexibleInstances和UndecidableInstances类型扩展).这个工作正常,当我只有一个A或B,但当我尝试与两者编译时,我得到以下错误:
Duplicate instance declarations:
instance [overlap ok] (A a, Num x, Show (a x), Eq (a x)) =>
Num (a x)
-- Defined at test.hs:13:10-56
instance [overlap ok] (B b, Num x, Show (b x), Eq (b x)) =>
Num (b x)
-- Defined at test.hs:27:10-56
Run Code Online (Sandbox Code Playgroud)
我想,"重复实例声明"的消息是因为一个数据类型可以由这两个实例A和B.我希望能够向编译器做出我不会这样做的承诺,或者可能指定在类型是两个类的实例的情况下使用的默认类.
有没有办法做到这一点(也许是另一种类型的扩展?)或者这是我坚持的事情?
这是我的代码:
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
class A a where
fa :: a x -> x
ga :: x -> a x
data AImpl x = AImpl x deriving (Eq,Show)
instance A AImpl where
fa (AImpl x) = x
ga x = AImpl x
instance (A a, Num x, Show (a x), Eq (a x)) => Num (a x) where
a1 + a2 = ga (fa a1 + fa a2)
-- other implementations go here
class B b where
fb :: b x -> x
gb :: x -> b x
data BImpl x = BImpl x deriving (Eq,Show)
instance B BImpl where
fb (BImpl x) = x
gb x = BImpl x
instance (B b, Num x, Show (b x), Eq (b x)) => Num (b x) where
-- implementations go here
Run Code Online (Sandbox Code Playgroud)
编辑:为了使自己清楚,我不是试图用这种技术编写任何实用的代码.我这样做是为了帮助自己更好地理解类型系统和扩展.
Joh*_*n L 12
这部分问题
我认为'重复实例声明'消息是因为数据类型可以成为A和B的实例.我希望能够向编译器做出我不会这样做的承诺,或者可能指定一个在类型是两个类的实例的情况下使用的默认类.
是不正确的.这实际上是因为你写了两个实例,
instance Num (a x)
instance Num (b x)
Run Code Online (Sandbox Code Playgroud)
编译器无法区分(参见@ hammar注释中的链接,类上下文不计入区分实例声明的目的).
一种解决方案是添加见证类型.
{-# LANGUAGE FlexibleInstances, FlexibleContexts, UndecidableInstances, OverlappingInstances #-}
data AWitness
data AImpl witness x = AImpl x deriving (Eq,Show)
instance A (AImpl AWitness) where
fa (AImpl x) = x
ga x = AImpl x
instance (A (a AWitness), Num x, Show (a AWitness x), Eq (a AWitness x)) => Num (a AWitness x) where
a1 + a2 = ga (fa a1 + fa a2)
Run Code Online (Sandbox Code Playgroud)
编译器可以使用见证类型来区分您的实例声明.
没有真正好的方法可以做到这一点; 最好的做法是定义一些常量
plusA, minusA :: (A a, Num x) => a x -> a x -> a x
Run Code Online (Sandbox Code Playgroud)
这使得Num在拥有A实例后编写实例更具机械性:
instance A Foo where ...
instance Num x => Num (Foo x) where
(+) = plusA
(-) = minusA
Run Code Online (Sandbox Code Playgroud)