3

假设我有一个幺半群定义如下:

data TotalLine = TotalLine { totalQuantity :: Int, orderTotal :: Float }

instance Monoid TotalLine where
  mempty = zero
  mappend = add

由于totalQuantityorderTotal都是数字,它们还在(+)下形成了一个幺半群。 有什么方法可以定义

add :: TotalLine -> TotalLine -> TotalLine

所以,我可以只在每个字段上传播mappend调用,而不必手动定义类似

add line1 line2 =
  TotalLine {
    totalQuantity = totalQuantity line1 + totalQuantity line2,
    orderTotal = orderTotal line1 + orderTotal line2
   }
4

3 回答 3

7

请注意,有几个具有域 Int(Nat、Float、..)的幺半群,所以我会发现阅读具有instance Monoid Int. 然后mappend可以是加号、时间、最大值、最小值等等中的任何一个。

于 2016-03-18T15:37:45.793 回答
4

generic-deriving包完全符合您的要求:

{-# LANGUAGE DeriveGeneric #-}

import           Generics.Deriving.Monoid
import           GHC.Generics

data T = T {str :: String, str' :: String}
  deriving (Generic, Show)

main = undefined

instance Monoid T where
  mempty = memptydefault
  mappend = mappenddefault

还有一些来自 ghci 的结果:

> T "a" "b" `mappend` T "c" "d"
< T {str = "ac", str' = "bd"}

但它不适用于您的TotalLine数据类型,因为两者IntFloat没有Monoid.

还为此目的添加依赖项并不那么经济。您最好Monoid手动实现该实例。


关于为什么 GHC 不能派生 Monoid 实例,已经有一些讨论,结果证明,一般来说,这样的派生实例可能不是唯一的。但是在特殊情况下,当数据类型只有一个具有具体和单曲面字段的构造函数时,派生实例是唯一的。

于 2016-03-18T15:36:08.153 回答
3

如果您可以稍微更改类型(但这仍然与您的类型同构),您可以试试这个:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Data.Monoid

newtype TotalLine = TotalLine (Sum Int, Sum Float) deriving Monoid
于 2016-03-18T16:55:30.557 回答