Почему бездельник оценивает содержание моноида

Почему

(mempty :: String)

работать, но

(mempty :: Maybe Bool)

выдает ошибку Bool не имеющий тип класса Monoid, но почему это даже имеет значение, это завернуто в Может быть, так или иначе?

2 ответа

Для списков, [a] это Monoid несмотря ни на что:

instance {- no requirements on a here! => -} Monoid [a] where
    ...

Так String является моноидом без дальнейшей проверки свойств Char, Но Maybe a это только пример Monoid если a это:

instance Monoid a => Monoid (Maybe a) where
    ...

Таким образом, для Maybe Bool чтобы быть моноидом, мы должны проверить, что Bool это моноид Это полностью объясняет ваше сообщение об ошибке.

Дополнительный вопрос номер один: почему a должен быть Monoid? Это так, что мы можем написать mappend:

    Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)

Конечно, вам действительно нужно только Semigroup для этого; но эта тонкая разница не имеет значения, потому что Bool не является Semigroup, или.

Второй вопрос: почему нет Bool Monoid? Потому что есть несколько хороших примеров, и непонятно, что "благословить" как официальный пример. Таким образом, экземпляры прикреплены к newtype с над Bool; два наиболее распространенных Any а также All,

Дополнительный вопрос номер три: каковы ваши альтернативы? Вы могли бы First или же Last для моноидов, которые держат первый (соответственно последний) Just они видят. Существует также стандартный экземпляр и экземпляр, где mempty = pure mempty; mappend = liftA2 mappend хотя я не знаю стандартного места для этого.

Это Monoid экземпляр для Maybe:

instance Monoid a => Monoid (Maybe a) where
  mempty = Nothing
  Nothing `mappend` m = m
  m `mappend` Nothing = m
  Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)

Требуется, чтобы базовый тип имел Monoid экземпляр, потому что его собственный mappend делегаты к тому из базового типа. Обратите внимание, что это Monoid ограничение является излишне ограничительным, так как основной mempty на самом деле не используется. Более уместный взгляд на это предоставляется как Option в Data.SemigroupSemigroup это Monoid без mempty):

-- Paraphrasing the relevant bits of the source.
instance Semigroup a => Semigroup (Maybe a) where
  Nothing <> b       = b
  a       <> Nothing = a
  Just a  <> Just b  = Just (a <> b)

newtype Option a = Option { getOption :: Maybe a }

instance Semigroup a => Semigroup (Option a) where
  Option ma <> Option mb = Option (ma <> mb)

instance Semigroup a => Monoid (Option a) where
  mempty = Option Nothing
  mappend = (<>)

Если вы хотите Monoid за Maybe который не заботится о базовом типе, взгляните на упаковщики нового типа, предоставленные Data.Monoid: First (или же Alt Maybe) для левостороннего и Last (или же Dual (Alt Maybe)) для предвзятого.

-- For instance, this is the First monoid:
newtype First a = First { getFirst :: Maybe a }

instance Monoid (First a) where
        mempty = First Nothing
        First Nothing `mappend` r = r
        l `mappend` _             = l
GHCi> import Data.Monoid
GHCi> First Nothing <> First Nothing
First {getFirst = Nothing}
GHCi> First (Just 3) <> First Nothing
First {getFirst = Just 3}
GHCi> First Nothing <> First (Just 3)
First {getFirst = Just 3}
GHCi> First (Just 3) <> First (Just 4)
First {getFirst = Just 3}
GHCi> Last (Just 3) <> Last (Just 4)
Last {getLast = Just 4}
GHCi> Alt (Just 3) <> Alt (Just 4)
Alt {getAlt = Just 3}
GHCi> Dual (Alt (Just 3)) <> Dual (Alt (Just 4))
Dual {getDual = Alt {getAlt = Just 4}}

(Кстати, Alt делегаты mempty а также mappend к Alternative экземпляр Maybe, что эквивалентно тому, что Алек предлагает в комментарии.)

Другие вопросы по тегам