让我们看看Haskell中用于处理反向列表的新数据类型的声明:
import Data.Monoid
data RevList a = Nil | RCons (RevList a) a deriving (Eq, Show)
instance Monoid a => Monoid (RevList a) where
mempty = Nil
instance Semigroup a => Monoid (RevList a) where
Nil <> RCons (RevList a) a = RCons (RevList a) a
RCons (RevList a) a <> RNil = RCons (RevList a) a
Nil <> Nil = Nil
我困扰的问题是编译失败,其描述如下:
`<>' is not a (visible) method of class `Monoid'
首先,我尝试创建没有任何Semigroup实例声明的Monoid实例,但是在读取this question之后导致了另一个失败。那么,当前内容中的“ <>”怎么了?可以肯定的是,我知道缺少像Mappend或mconcat这样的Monoid函数,它会作为强制性函数添加到Monoid实例代码中。
答案 0 :(得分:5)
定义<>
的实例应该用于Semigroup (RevList a)
,而不是Monoid (RevList a)
,因为<>
是Semigroup
方法。
我知道缺少像mappend或mconcat这样的Monoid函数,它们会作为强制性函数添加到Monoid实例代码中
它们实际上不是强制性的,您可以通过
来告知在http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Monoid.html中最小完整定义
mempty
。
您实际上并没有在实例中使用约束(=>
之前的部分);例如您没有在实现mempty :: a
的过程中调用mempty :: RevList a
。因此它们可以被删除,您最终会得到
instance Monoid (RevList a) where
mempty = RNil
instance Semigroup (RevList a) where
RNil <> RCons (RevList a) a = RCons (RevList a) a
RCons (RevList a) a <> RNil = RCons (RevList a) a
RNil <> RNil = RNil
答案 1 :(得分:1)
instance Semigroup [a] where
(<>) = (++)
让我们不必定义
append (x:xs) ys = x : append xs ys -- x is the first
append [] ys = ys
要处理与反向列表的表示形式相同的常规列表数据类型(x:xs)
,并在其末尾带有x
,我们必须定义:
apprev xs (y:ys) = y : apprev xs ys -- y is the last
apprev xs [] = xs
(因此,实际上apprev == flip append
!-也就是说,我们基本上只是在镜子里看同一件事。)
类型是由其相互作用而不是表示来定义的。如果旧数据类型与新数据类型完全同构(可以很好地表示),则不需要全新的数据类型定义。只需一个newtype
标签就足以表明附加操作的不同行为:
newtype RevList a = Rev [a]
instance Semigroup (RevList a) where
Rev xs <> Rev [] = Rev xs
Rev xs <> Rev (y:ys) = Rev (y:zs)
where
Rev zs = Rev xs <> Rev ys
(注意:您的定义无法处理所有可能的情况。它还会将Nil
和RNil
混合在一起。)