我们说我有一个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
}
答案 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
数据类型,它不会开箱即用,因为Int
和Float
都没有Monoid
的实例。< / p>
同样为此目的添加一个依赖并不那么经济。您最好手动实现Monoid
实例。
为什么GHC无法派生Monoid实例已经some discussion,事实证明这样的派生实例可能并不是唯一的。但在特殊情况下,当数据类型只有一个带有混凝土的构造函数时和monoidal字段派生的实例是唯一的。
答案 2 :(得分:3)
如果您稍微改变一下类型(但仍然与您同构),您可以试试这个:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Data.Monoid
newtype TotalLine = TotalLine (Sum Int, Sum Float) deriving Monoid