Are Maybe String and Maybe Int both Monoids in Haskell?

244 Views Asked by At

I´ve gotten into a bit of confusion whether Maybe Int and Maybe String are both monoids in Haskell because i dont really know what functions you can apply to Int or String in terms of monoids with their neutral element and associativity rules! Can anybody help me out?

2

There are 2 best solutions below

0
willeM_ Van Onsem On

The Monoid instance for Maybe types has been defined as [src]:

instance Semigroup a => Monoid (Maybe a) where
    mempty = Nothing

it thus requires Int or String to be instances of the Semigroup type class. Since a String is the same as type String = [Char] and thus a list of Chars, this is the case, indeed we see [src]:

instance Semigroup [a] where
        (<>) = (++)
        {-# INLINE (<>) #-}

        stimes = stimesList

so it will append the two lists, and for a Maybe this means [src]:

instance Semigroup a => Semigroup (Maybe a) where
    Nothing <> b       = b
    a       <> Nothing = a
    Just a  <> Just b  = Just (a <> b)

    stimes = stimesMaybe

This thus means that for two Maybe Strings, the neutral element is Nothing and the binary operator will append the two strings wrapped in Just … data constructors if these are both Justs, the Just one if one of the two is a Just, or Nothing if both items are Nothings.

This is not the case for an Int, indeed. For integers there can be multiple Monoid instances, for example ⟨ℤ,+, 0⟩ or ⟨ℤ,×, 1⟩.

3
chepner On

There is a Monoid instance for any Maybe a as long as a itself has a Monoid instance; the combination of two Just values simply combines the wrapped values according to their instance, while anything combined with Nothing is itself Nothing.

String has a Monoid instance by virtue of being an alias of [Char]; all lists are monoids via list concatenation with the empty list as the identity. As a result, Maybe String is a monoid as well.

> Just "Fo" <> Just "o"
Just "Foo"
> Just "Fo" <> Nothing
Just "Fo"

Maybe Int is not a monoid, because Int is not a monoid. This is because there are multiple choices for how to make Int a monoid, so no one choice is chosen as special. Instead, several new types are defined with distinct Monoid instances to clarify which operation you want to use.

> import Data.Monoid
> (3 :: Int) <> 5

<interactive>:8:1: error:
    • No instance for (Semigroup Int) arising from a use of ‘<>’
    • In the expression: (3 :: Int) <> 5
      In an equation for ‘it’: it = (3 :: Int) <> 5

-- (+) and 0
> Sum (3 :: Int) <> Sum 5
Sum {getSum = 8}

-- (*) and 1
> Product (3 :: Int) <> Product 5
Product {getProduct = 15}

> Just (Sum (3 :: Int)) <> Just (Sum 5)
Just (Sum {getSum = 8})
> Just (Sum (3 :: Int)) <> Nothing
Just (Sum {getSum = 3})