Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Haskell中,是否有任何方法可以用多种方式表示类型应该是typeclass的实例?_Haskell_Typeclass - Fatal编程技术网

在Haskell中,是否有任何方法可以用多种方式表示类型应该是typeclass的实例?

在Haskell中,是否有任何方法可以用多种方式表示类型应该是typeclass的实例?,haskell,typeclass,Haskell,Typeclass,(如果问题愚蠢或明显,请提前道歉——我对Haskell没有太多经验) 有没有一种方法可以表示一个类型应该以多种方式成为一个typeclass的实例?最好用一个例子来说明这一点(这可能有点傻):在数学中,我们可以说半环是一个集合,在一个运算(我们称之为加法,恒等式0)下是交换幺半群,在另一个运算(我们称之为乘法)下是幺半群以及乘法分布在加法上,0湮灭乘法下的所有元素的要求。后面的部分在这里并不重要 假设现在我有一个typeclassMonoid(不要与Data.Monoid)混淆 并希望创建一个t

(如果问题愚蠢或明显,请提前道歉——我对Haskell没有太多经验)

有没有一种方法可以表示一个类型应该以多种方式成为一个typeclass的实例?最好用一个例子来说明这一点(这可能有点傻):在数学中,我们可以说半环是一个集合,在一个运算(我们称之为加法,恒等式0)下是交换幺半群,在另一个运算(我们称之为乘法)下是幺半群以及乘法分布在加法上,0湮灭乘法下的所有元素的要求。后面的部分在这里并不重要

假设现在我有一个typeclass
Monoid
(不要与
Data.Monoid
)混淆

并希望创建一个typeclass
半环
。根据上面给出的定义,我想说“如果r型在两种(不同的)方式上是幺半群,我们称之为半环”。所以我想要一些

class (Monoid r, Monoid r) => Semiring r where ...
这当然行不通。诚然,这个例子有点奇怪,因为我们不想再需要半环函数了,所以typeclass应该是空的,但我希望它能说明我的问题(或者假装我们需要一些函数
f:r->r
半环r


因此,在一般设置中,我在问:给定一个typeclass
a
,是否有一种方法可以参数化typeclass
B a
,要求
a
以两种方式成为
a
的实例(意味着
a
应该以两种方式实现
a
指定的功能)?

对于Monoid,具体来说,这是通过类型包装器完成的。如果查看模块
Data.Monoid
,您会发现
Bool
值有两种不同的Monoid结构:
Any
All
,以及实现
Num
的类型的两种不同结构:
Sum
Product
,以及
可能
类型的两种结构:
First
Last


但是,您的半环示例会遇到问题,因为
Sum
Product
的幺半体结构都提供了
mempty
(Haskell版本的
单元
)和
mappend
(Haskell版本的
操作
).

一个选项是为半环的两个操作定义自己的幺半群:

class AdditiveMonoid m where
    zero :: m
    (<+>) :: m -> m -> m

class MultiplicativeMonoid m where
    one :: m
    (<*>) :: m -> m -> m
问题是你不能表达幺半群定律或者一个运算是可交换的。您所能得到的最好结果就是为规则定义快速检查属性

要获得一些灵感,请查看和论文。

另请参阅Conor McBride的“节奏部分”帖子:,尽管这是在值级别,对类型类没有帮助

Kmett的Monoids库(在他剥离戒指之前)实现了类似于Daniel Velkov的方法:


我应该补充一点,这种方法的好处在于,通过明确定义数据类型上的加法和乘法,可以发现它们是不同的——即后者分布在前者之上。

如其他答案所述,这种方法的常用技术是新类型包装器。在很多情况下,我觉得这是对类型类概念的滥用。类型类是逻辑上的“公理”,表明某个类型的某些事实是正确的;例如,这可能是一个Monad,或者Int是Num,或者列表的元素是有序的。通常,在Eq和Ord的情况下,有其他合理的定义,但选择的是某种程度上的“规范”。其他时候,就像在幺半群的情况下一样,没有

对于幺半群和其他类似的高度抽象结构,我认为
数据
声明更有用。例如,
datamonoida=Monoid{mempty::a;mappend::a->a->a}
。然后,我们有
addition::Num a=>monoida
liftMaybe::monoida->Monoid(可能是a)
,等等


同样的技术也可以用于实现半环。例如(像以前一样使用幺半群数据类型):
数据半环a=Semiring{加法,乘法::幺半群a}
其他答案提到了
newtype
包装器,但没有给出使用它们的显式解决方案:

-- export these newtypes from the module defining Semiring
newtype Add a = Add a
newtype Multiply a = Multiply a

class (Monoid (Add a), Monoid (Multiply a)) => Semiring a where
  -- empty

instance Monoid (Add Integer) where
  unit = Add 0
  Add a `operation` Add b = Add (a + b)

-- etc.

您需要一些GHC扩展,如
FlexibleContexts

Hmm。。。谢谢你提供的信息,但听起来答案似乎归结为“不”,那么?真可惜:-(事实上,如果我理解正确,这基本上就是GHC在幕后实现类型类多态性所做的。不过,我不相信用户级代码的好处。感谢显式的代码示例。它让一切变得更加清晰。尽管我有点感觉,我经常会对什么时候应该e类型类以及何时应该生成类型。事实上,为了解决这个问题,我自己已经将类型类添加和乘法。你的方法似乎更好,但这意味着我对什么是“应该”类型类和什么是“应该”类型之间的关系有一些错误的理解。也许我会提出另一个问题:-)感谢到目前为止所有回答的人。几乎任何一个答案都可以被接受,但我选择了得票最多的一个。非常感谢!似乎简短的回答是“不,你必须重复代码”,那么?我知道数字的前奏曲,但它太大了,当你是新手时,很难从中学习。不过,那篇论文看起来很有趣,谢谢!
class (MultiplicativeMonoid m, AdditiveMonoid m) => Semiring m
-- export these newtypes from the module defining Semiring
newtype Add a = Add a
newtype Multiply a = Multiply a

class (Monoid (Add a), Monoid (Multiply a)) => Semiring a where
  -- empty

instance Monoid (Add Integer) where
  unit = Add 0
  Add a `operation` Add b = Add (a + b)

-- etc.