3

让我们看看在 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 实例,但在阅读此问题后导致了另一个失败。那么,当前的东西中的 '<>' 有什么问题呢?可以肯定的是,我知道缺少像 mappend 或 mconcat 这样的 Monoid 函数作为强制添加到 Monoid 实例代码中。

4

2 回答 2

5

实例定义<>应该是 for Semigroup (RevList a),而不是 for 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
于 2020-01-01T08:27:33.947 回答
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。)

于 2020-01-01T10:25:56.360 回答