(如果问题很愚蠢或明显,请提前抱歉 - 我对Haskell没有太多经验).
有没有办法用多种方式表示类型应该是类型类的实例?用一个例子(这可能有点傻)最好地说明了这一点:在数学中,我们可以说半环是一个在一个操作(我们称之为加法,身份0)和一个幺半群下的交换幺半群的集合.另一个(我们称之为乘法)以及乘法在加法上分配的要求,并且0在乘法下消灭所有元素.后面的部分在这里并不重要.
假设我现在有一个类型类Monoid(不要混淆Data.Monoid),
class Monoid m where
unit :: m
operation :: m -> m -> m
Run Code Online (Sandbox Code Playgroud)
并且想要创建一个类型类Semiring.从上面给出的定义来看,我想说"如果类型r是两种(不同的)方式的幺半群,我们称之为半环".所以我喜欢类似的东西
class (Monoid r, Monoid r) => Semiring r where ...
Run Code Online (Sandbox Code Playgroud)
这当然不起作用.不可否认,这个例子变得有点奇怪,因为我们不再需要semirings的函数,所以类型类是空的,但我希望它能说明我所要求的(或者只是假装我们需要一些函数)f:r->rfor Semiring r).
所以,在一般设置中,我问:给定一个类型类A,是否有一种方法可以参数化一个类型类B a,其要求a是A两种方式的实例(意味着a应该以两种方式实现指定的函数A)?
Dan*_*kov 12
一种选择是为半环的两个操作定义自己的幺半群:
class AdditiveMonoid m where
zero :: m
(<+>) :: m -> m -> m
class MultiplicativeMonoid m where
one :: m
(<*>) :: m -> m -> m
Run Code Online (Sandbox Code Playgroud)
然后合并它们:
class (MultiplicativeMonoid m, AdditiveMonoid m) => Semiring m
Run Code Online (Sandbox Code Playgroud)
问题是你不能表达幺半群定律或一个操作是可交换的这一事实.您可以获得的最好的方法是定义法律的快速检查属性.
对于Monoid,具体来说,这是使用类型包装器完成的.如果你看看到模块中Data.Monoid,你会发现两个不同的monoidal结构Bool值:Any和All为实现类型,以及两种不同的结构Num:Sum和Product并两种结构Maybe类型:First和Last.
但是,你的semiring示例会遇到问题,因为monoidal结构Sum和(Product都提供mempty了你的Haskell版本unit)和mappend(你的Haskell版本operation)的实现.
其他答案提到了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.
Run Code Online (Sandbox Code Playgroud)
您需要一些GHC扩展,例如FlexibleContexts。