为记录类型定义幺半群实例

use*_*931 5 haskell typeclass monoids

假设我有一个类型

data Options = Options
  { _optionOne :: Maybe Integer
  , _optionTwo :: Maybe Integer
  , _optionThree :: Maybe String
  } deriving Show
Run Code Online (Sandbox Code Playgroud)

有更多的领域。我想Monoid为这种类型定义一个实例,其mempty值是 an Optionswith all fields Nothing。有没有比这更简洁的写法

instance Monoid Options where
  mempty = Options Nothing Nothing Nothing
  mappend = undefined
Run Code Online (Sandbox Code Playgroud)

Nothing当我Options有更多字段时,这将避免需要写一堆s ?

Jon*_*rdy 5

我建议只写Nothings,甚至明确拼出所有记录字段,这样您就可以确保在添加具有不同mempty值的新字段或重新排序字段时不会错过任何情况:

mempty = Options
  { _optionOne = Nothing
  , _optionTwo = Nothing
  , _optionThree = Nothing
  }
Run Code Online (Sandbox Code Playgroud)

我以前没有尝试过,但似乎您可以为此目的使用泛型派生包,只要您记录的所有字段都是Monoids。您将添加以下语言编译指示和导入:

{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics (Generic)
import Generics.Deriving.Monoid
Run Code Online (Sandbox Code Playgroud)

添加deriving (Generic)到您的数据类型并将所有非Monoid字段包装在一个类型中,Data.Monoid使用您想要的组合行为,例如First, Last, Sum, 或Product

data Options = Options
  { _optionOne :: Last Integer
  , _optionTwo :: Last Integer
  , _optionThree :: Maybe String
  } deriving (Generic, Show)
Run Code Online (Sandbox Code Playgroud)

例子:

  • Last (Just 2) <> Last (Just 3) = Last {getLast = Just 3}
  • First (Just 2) <> First (Just 3) = First {getFirst = Just 2}
  • Sum 2 <> Sum 3 = Sum {getSum = 5}
  • Product 2 <> Product 3 = Product {getProduct = 6}

然后使用以下函数 fromGenerics.Deriving.Monoid来创建您的默认实例:

memptydefault :: (Generic a, Monoid' (Rep a)) => a
mappenddefault :: (Generic a, Monoid' (Rep a)) => a -> a -> a
Run Code Online (Sandbox Code Playgroud)

在上下文中:

instance Monoid Options where
  mempty = memptydefault
  mappend = ...
Run Code Online (Sandbox Code Playgroud)