4
votes

Apologies if I am using the wrong name for things. My question comes from contrasting Scala and Haskell syntax. Consider:

class Monoid a where
  mempty :: a
  mappend :: a -> a -> a

instance Monoid Int where 
  mempty = 0
  mappend a b = a + b

sigma :: (Monoid a) => Int -> Int -> (Int -> Int) -> (Int -> a) -> a
sigma a b inc comp =
  if a > b then mempty else mappend (comp a) (sigma (inc a) b inc comp)

In Scala it would probably be something like:

trait Monoid[A] {
  def mempty: A
  def mappend(a1: A, a2: A): A
}

class IntMonoid extends Monoid[Int] {
  def mempty = 0
  def mappend(a: Int, b: Int) = a + b
}

def sigma[A](a: Int, b: Int, inc: Int => Int, comp: Int => a)
            (implicit m: Monoid[A]): A =
  if (a > b) m.mempty else m.append(comp(a), sigma(inc(a), b, inc, comp))

Now, Int can be a Monoid with 0 and addition, but also with 1 and multiplication, so we can provide 2 type classes, one for each implementation. In Scala, if both implementations are implicit within the scope and have the same priority, this will cause a compile error. We can simple manually pass the correct instance in this case, and the error will be resolved.

What is the Haskell equivalent for this situation? If there are two instances of Int being Monoid, how can one choose which implementation to use?

2
An irrelevant nitpick: there is no need to exclude zero if you want to see Int,*,1 as monoid.chi
You're correct, was thinking about groups. Corrected the question, thanks!Henry Henrinson

2 Answers

12
votes

Haskell simply has two newtype wrapper for any type that actually is an instance of Num (includes Int type) typeclass: Sum and Product. So, Sum is a monoid under addition and Product type is a monoid under multiplication. Taken from actual source:

newtype Sum a = Sum { getSum :: a }
        deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1, Num)

instance Num a => Monoid (Sum a) where
        mempty = Sum 0
        Sum x `mappend` Sum y = Sum (x + y)

newtype Product a = Product { getProduct :: a }
        deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1, Num)

instance Num a => Monoid (Product a) where
        mempty = Product 1
        Product x `mappend` Product y = Product (x * y)
4
votes

You can't have two instances for the same type and typeclass within a module in Haskell. If you have two instances in two different modules and then import both of those modules, trying to use the instance will cause a compilation error.

So the only way to select which instance to use, would be to only import only import one module that provides the instance.