玩转类型! 解析多个实例声明(Fun with types! Resolving multipl

2019-06-23 09:49发布

我尝试写一些Haskell代码,其中有多种数据类型,每一种都可以有多种实现。 要做到这一点,我定义每种数据类型的class ,它的方法是相关构造函数和选择,然后实现在给定的构造函数和选择方面对类的成员的所有操作。

例如,或许A是多项式类(方法getCoefficientsmakePolynomial ),其可以具有表示作为SparsePolyDensePolyB是复数类(方法getRealgetImagmakeComplex ),其可以被表示为ComplexCartesianComplexPolar

我抄录如下小例子。 我有两个班AB各自有一个实现。 我想使这两个类的所有实例进入的情况下, Num自动(这需要FlexibleInstancesUndecidableInstances类型扩展名)。 这工作得很好,当我只有一个AB ,但是当我试图用两个编译,我得到以下错误:

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

我想的是,“重复实例声明”消息是因为一个数据类型可以由两者的实例AB 。 我希望能够做出一个承诺,我不会那么做的,或者可能指定默认的类在一个类型既是类的实例的情况下使用编译器。

有没有办法做到这一点(也许另一种类型的扩展名?),或者这个东西,我坚持了?

这里是我的代码:

{-# 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

编辑:为了使自己清楚,我不是要写使用这种技术的任何实际的代码。 我在做一个练习,以帮助自己了解的类型系统和扩展更好。

Answer 1:

你的问题的这部分

我想,“重复实例声明”的消息是因为一个数据类型可以由A和B两个实例我希望能够做出承诺,编译器,我不会那么做的,或者可能指定默认类在一个类型是既类的实例的情况下使用。

是不正确的。 它实际上是因为你写的两个实例,

instance Num (a x)
instance Num (b x)

编译器无法分辨(请参阅从@哈马尔的评论的链接,类上下文不计的实例声明区分的目的)。

一个解决办法是增加一个见证类型。

{-# 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)

编译器可以使用的见证类型的实例声明之间进行区分。



Answer 2:

有做到这一点真的没有好办法; 最好的做法是定义一些常量像

plusA, minusA :: (A a, Num x) => a x -> a x -> a x

这使得编写Num实例的详细机械您有一个后A实例:

instance A Foo where ...
instance Num x => Num (Foo x) where
    (+) = plusA
    (-) = minusA


文章来源: Fun with types! Resolving multiple instance declarations