简体   繁体   中英

Derive monoid instance for custom `Maybe` data type

For learning purposes, i am trying to create an Optional data type that mimics Maybe in the typeclass instance of Monoid where

-- Data.Monoid> Prelude.mappend (Just [1,2]) (Just [3,4])
-- Just [1,2,3,4]

Instead, I am getting the behavior of the First instance of Maybe for Monoid .

-- Data.Monoid> (First (Just [1,2])) <> (First (Just [3,4]))
-- First {getFirst = Just [1,2]}

My data type and typeclass instance are:

data Optional a =
  Nada
  | Only a
  deriving (Eq, Show)

 instance Monoid a
  => Monoid (Optional a) where
mempty = Nada
mappend x mempty = x
mappend mempty (Only x) = Only x
mappend (Only x) (Only y) = Only (Prelude.mappend x y)

My questions are

  • Why does it behave like this?
  • How do I get mappend for Optional to behave like the instance for Maybe that concatenates lists rather than taking the first Only value?

Why does it behave like this?

Because that is how First has been designed. The idea of First is to "wrap" a Maybe a value, and to implement the Monoid like:

instance Monoid (First a) where
    mempty = First Nothing
    mappend l@(First (Just _)) _ = l
    mappend _ x = x

It thus returns the first First constructor that wrapps a value Just data constructor. This can be useful when we use the mconcat :

Prelude Data.Monoid> mconcat [First Nothing, First (Just 0), First Nothing, First (Just 118), First (Just 999)]
First {getFirst = Just 0}

We thus do not use the Monoid (Maybe a) implementation, the First decides how the Monoid looks like. A similar thing happens with Sum and Product

How do I get mappend for Optional to behave like the instance for Maybe that concatenates lists rather than taking the first Only value?

There is an error in your definition. You use mempty in the head of a function like:

mappend x  = x

is just a variable named mempty . So the first line will match everything . You should fix it, like you intend with:

instance Monoid a => Monoid (Optional a) where
    mempty = Nada
    mappend x  = x
    mappend  x = x
    mappend (Only x) (Only y) = Only (Prelude.mappend x y)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM