有趣的类型!解决多个实例声明 [英] Fun with types! Resolving multiple instance declarations

查看:100
本文介绍了有趣的类型!解决多个实例声明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图编写一些Haskell代码,其中有多个数据类型,每个数据类型可以有多个实现。为此,我将每个数据类型定义为一个 class ,它们的方法是相关的构造函数和选择器,然后根据给定的构造函数对该类的成员执行所有操作例如,也许 A 是一个多项式类(方法 getCoefficients 和 makePolynomial ),它可以表示为 SparsePoly DensePoly B 是一个复数类(方法 getReal getImag makeComplex ),可以表示为 ComplexCartesian 或a ComplexPolar



我已经在下面再现了一个简单示例。我有两个类 A B ,每个类都有一个实现。我希望自动将这两个类的所有实例变为 Num 的实例(这需要 FlexibleInstances UndecidableInstances 类型扩展)。当我只有一个 A B 时,这工作正常,但是当我尝试使用两者进行编译时,出现以下错误:

pre $ code $重复实例声明
实例[overlap ok](A a,Num x,Show( ax),Eq(ax))=>
Num(ax)
- 定义于test.hs:13:10-56
实例[overlap ok](B b,Num x,Show(bx),Eq(bx) )=>
Num(bx)
- 定义于test.hs:27:10-56

我想'重复实例声明'的消息是因为数据类型可以作为 A B 。我希望能够向编译器作出承诺,我不会那样做,或者可能指定一个默认类用于类型是两个类的实例的情况。



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



这是我的代码:

  { - #LANGUAGE FlexibleInstances,UndecidableInstances,OverlappingInstances# - } 
$ b $ class A a where
fa :: ax - > x
ga :: x - > ax

data AImpl x = AImpl x派生(Eq,Show)

实例A AImpl其中
fa(AImpl x)= x
ga x (A a,Num x,Show(ax),Eq(ax))=> = Amp1×

实例Num(ax)其中
a1 + a2 = ga(fa a1 + fa a2)
- 其他实现去这里


class B b其中
fb :: bx - > x
gb :: x - > bx

data BImpl x = BImpl x派生(Eq,Show)

实例B BImpl其中
fb(BImpl x)= x
gb x (B b,Num x,Show(bx),Eq(bx))=> Bmpl×

实例Num(bx)其中
- 执行到这里

编辑: 解决方案

/ p>


我想'重复的实例声明'消息是因为数据类型可以作为A和B的一个实例。我想能够向编译器作出承诺,我不会那样做,或者可能指定一个默认类用于类型是两个类的实例的情况。


不正确。这实际上是因为你已经写了两个实例,

pre $ c $实例Num(ax)
实例Num(bx)

编译器无法区分(请参阅@ hammar评论中的链接,类上下文不计算在内以区分实例声明)。

一种解决方案是添加证明类型。



<$ p $
$ b数据AWitness

数据AImpl见证x = AImpl x推导(Eq) ,显示)

实例A(AImpl AWitness)其中
fa(AImpl x)= x
ga x = AImpl x

实例(A (AWitness),Num x,Show(AWitness x),Eq(AWitness x))=> Num(AWitness x)其中
a1 + a2 = ga(fa a1 + fa a2)

编译器可以使用证明类型来区分实例声明。


I'm trying to write some Haskell code in which there are multiple data types, each of which can have multiple implementations. To do this, I define each data type as a class whose methods are the relevant constructors and selectors, and then implement all operations on members of that class in terms of the given constructors and selectors.

For example, perhaps A is a polynomial class (with methods getCoefficients and makePolynomial) which can have a representation as a SparsePoly or a DensePoly and B is a complex number class (with methods getReal, getImag and makeComplex) which can be represented as a ComplexCartesian or a ComplexPolar.

I've reproduced a minimal example below. I have two classes A and B each of which has an implementation. I want to make all instances of both classes into instances of Num automatically (this requires the FlexibleInstances and UndecidableInstances type extensions). This works fine when I only have one of A or B, but when I try to compile with both, I get the following error:

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

I suppose that the 'duplicate instance declarations' message is because a data type could be made an instance of both A and B. I want to be able to make a promise to the compiler that I won't do that, or possibly specify a default class to use in the case that a type is an instance of both classes.

Is there a way to do this (maybe another type extension?) or is this something I'm stuck with?

Here's my code:

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

Edit: To make myself clear, I'm not trying to write any practical code using this technique. I'm doing it as an exercise to help myself understand the type system and extensions better.

解决方案

This part of your question

I suppose that the 'duplicate instance declarations' message is because a data type could be made an instance of both A and B. I want to be able to make a promise to the compiler that I won't do that, or possibly specify a default class to use in the case that a type is an instance of both classes.

is incorrect. It's actually because you've written two instances,

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

that the compiler can't tell apart (see the link from @hammar's comment, class contexts don't count for the purpose of differentiating between instance declarations).

One solution is to add a witness type.

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

The compiler can use the witness types to differentiate between your instance declarations.

这篇关于有趣的类型!解决多个实例声明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆