在Haskell中,是否有任何方法可以用多种方式表示类型应该是typeclass的实例?
(如果问题愚蠢或明显,请提前道歉——我对Haskell没有太多经验) 有没有一种方法可以表示一个类型应该以多种方式成为一个typeclass的实例?最好用一个例子来说明这一点(这可能有点傻):在数学中,我们可以说半环是一个集合,在一个运算(我们称之为加法,恒等式0)下是交换幺半群,在另一个运算(我们称之为乘法)下是幺半群以及乘法分布在加法上,0湮灭乘法下的所有元素的要求。后面的部分在这里并不重要 假设现在我有一个typeclass在Haskell中,是否有任何方法可以用多种方式表示类型应该是typeclass的实例?,haskell,typeclass,Haskell,Typeclass,(如果问题愚蠢或明显,请提前道歉——我对Haskell没有太多经验) 有没有一种方法可以表示一个类型应该以多种方式成为一个typeclass的实例?最好用一个例子来说明这一点(这可能有点傻):在数学中,我们可以说半环是一个集合,在一个运算(我们称之为加法,恒等式0)下是交换幺半群,在另一个运算(我们称之为乘法)下是幺半群以及乘法分布在加法上,0湮灭乘法下的所有元素的要求。后面的部分在这里并不重要 假设现在我有一个typeclassMonoid(不要与Data.Monoid)混淆 并希望创建一个t
Monoid
(不要与Data.Monoid
)混淆
并希望创建一个typeclass半环
。根据上面给出的定义,我想说“如果r型在两种(不同的)方式上是幺半群,我们称之为半环”。所以我想要一些
class (Monoid r, Monoid r) => Semiring r where ...
这当然行不通。诚然,这个例子有点奇怪,因为我们不想再需要半环函数了,所以typeclass应该是空的,但我希望它能说明我的问题(或者假装我们需要一些函数f:r->r
来半环r
)
因此,在一般设置中,我在问:给定一个typeclass
a
,是否有一种方法可以参数化typeclassB 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.