幺半群的自动传播

时间:2016-03-18 15:24:31

标签: haskell monoids

我们说我有一个monoid定义为:

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

instance Monoid TotalLine where
  mempty = zero
  mappend = add

由于 totalQuantity orderTotal 都是数字,它们也会在(+)下形成一个monoid 有没有办法定义

add :: TotalLine -> TotalLine -> TotalLine

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

的内容
add line1 line2 =
  TotalLine {
    totalQuantity = totalQuantity line1 + totalQuantity line2,
    orderTotal = orderTotal line1 + orderTotal line2
   }

3 个答案:

答案 0 :(得分:7)

注意有几个与域Int(Nat,Float,..)的幺半群 因此,我会发现阅读具有instance Monoid Int的代码非常困惑。然后mappend可以是加号,次数,最大值,最小值等等中的任何一个。

答案 1 :(得分: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的实例。< / p>

同样为此目的添加一个依赖并不那么经济。您最好手动实现Monoid实例。

为什么GHC无法派生Monoid实例已经some discussion,事实证明这样的派生实例可能并不是唯一的。但在特殊情况下,当数据类型只有一个带有混凝土的构造函数时和monoidal字段派生的实例是唯一的。

答案 2 :(得分:3)

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

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

newtype TotalLine = TotalLine (Sum Int, Sum Float) deriving Monoid