具有不同定义的mempty取决于mempty是左还是右arg?

The*_*Cat 0 haskell monoids semigroup

我有以下数据类型和半群实例:

data Or a b =
  Fst a
  | Snd b deriving (Eq, Show)

instance Semigroup (Or a b) where
  (<>) (Fst a) (Fst b) = Fst b
  (<>) (Snd a) (Fst b) = Snd a
  (<>) (Fst a) (Snd b) = Snd b
  (<>) (Snd a) (Snd b) = Snd a
Run Code Online (Sandbox Code Playgroud)

我想为上面的类型创建一个monoid实例,但我不知道如何做到这一点.如果我使用以下定义

instance (Monoid a, Monoid b) => Monoid (Or a b) where
  mempty = (Fst mempty)
  mappend = (<>)
Run Code Online (Sandbox Code Playgroud)

<>除了I之外,它将适用于所有输入对mappend

(Fst a) <> mempty
Run Code Online (Sandbox Code Playgroud)

将评估为mempty.

我该如何修复它以使其mempty有效?似乎没有一些新的语法或概念就无法做到,因为它取决于mempty是左边还是右边...

Dan*_*ner 7

有一个非常好(和更简单)的半群总是采用它的第一个参数:

newtype FirstS a = FirstS a
instance Semigroup (FirstS a) where
    a <> b = a
Run Code Online (Sandbox Code Playgroud)

不幸的是,它不是一个幺半群,因为 - 除了包装类型的微不足道的选择 - 这个操作没有遗留的身份.标准First类型补丁FirstS通过添加区分标识元素,因此:

newtype First a = First (Maybe a)
instance Semigroup (First a) where
    First Nothing <> b = b
    a <> First Nothing = a
    a <> b = a -- this clause is exactly as before
Run Code Online (Sandbox Code Playgroud)

然后Monoid通过选择来编写实例很容易mempty = First Nothing.您可以通过在类型中添加尊贵的标识元素来获得类似的技巧:

data Or a b = Fst a | Snd b | Neither
instance Semigroup (Or a b) where
    Neither <> b = b
    a <> Neither = a
    -- the remainder of the clauses are as before
Run Code Online (Sandbox Code Playgroud)

这使得选择mempty = Neither非常简单.

这种模式非常有用,它实际上有一个newtype包装器semigroups,因此您也可以使用原始Or a b类型简单地编写此修补类型Option (Or a b),SemigroupMonoid免费获取和实例.