如何解决Haskell代码错误“ <>'不是类Monoid”的(可见)方法?

时间:2020-01-01 07:40:01

标签: haskell monoids semigroup

让我们看看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实例代码中。

2 个答案:

答案 0 :(得分:5)

定义<>的实例应该用于Semigroup (RevList a),而不是Monoid (RevList a),因为<>Semigroup方法。

我知道缺少像mappend或mconcat这样的Monoid函数,它们会作为强制性函数添加到Monoid实例代码中

它们实际上不是强制性的,您可以通过

来告知

最小完整定义

mempty
http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Monoid.html

您实际上并没有在实例中使用约束(=>之前的部分);例如您没有在实现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

(注意:您的定义无法处理所有可能的情况。它还会将NilRNil混合在一起。)

相关问题