0
votes

I have following code snippet:

Prelude Data.Monoid> :t 1 <> (2 :: Sum Integer)
1 <> (2 :: Sum Integer) :: Sum Integer

How does 1 become to Sum Integer type?

Update

I play a bit with mappend function and discovered:

Prelude Data.Monoid> x = mappend 43

Why does mappend accept the number 43? 43 is a Num type and it has no monoid instance.

1
Look at the type for (<>).Code-Apprentice
Number literals in Haskell are overloaded using the Num typeclass. The :: Sum Integer on 2 tells Haskell that 2 :: Sum Integer has type Sum Integer (which has a Num instance). Then, since (<>) :: Monoid m => m -> m -> m, Haskell knows that 1 must have the same type as 2 :: Sum Integer.Alec
Why does 2 :: Sum Integer determine which type 1 has to be?softshipper
As stated by @Code-Apprentice, you should look at the type of <>, which is Monoid a => a -> a -> a. The types of both arguments must be identical.user2407038

1 Answers

5
votes

Firstly (2 :: Sum Integer).

Any literal integer in Haskell is rewritten by Haskell by wrapping it in a fromInteger call. So:

(2 :: Sum Integer) == ((fromInteger (2 :: Integer)) :: Sum Integer) 

Haskell just allows the notation on the left hand side so it's easier to type, but it's equivalent to the code on the right.

The fromInteger function has the following type signature:

fromInteger :: Num a => Integer -> a

So (fromInteger (2 :: Integer)) has type Num a => a

Somewhere in the libraries you'll have an instance like the following:

instance Num a => Num (Sum a) where ...

As Integer is a Num, so is Sum Integer.

So we can convert Num a => a to Sum Integer just by substituting a = Sum Integer.

Finally, we have the Semigroup function:

(<>) :: Semigroup a => a -> a -> a

There's also an instance:

instance Num a => Semigroup (Sum a) where ...

Somewhere in the semigroups modules, and as the (<>) function specifies that the left and right argument and result has to be the same argument, then if we know one of these we know the other two. We know that the right argument is Sum Integer, so we have to check we can convert the left argument to Sum Integer.

Remember from before, 1 really means fromInteger (1 :: Integer). We've mention before that there's a fromInteger implementation to Sum Integer, so this is fine. We can then use the semigroup operation (<>) on them as they're now the same type which is also a Semigroup.

Answer for update

Num is not a type, it's a class. Integer is a type, but as I've said before, 43 is not an Integer, it's fromInteger 43. It can be an Integer or it can be any Num type. Some of those Num types may be a Monoid.

Try x = mappend (43 :: Integer) and you'll find the code now fails to compile because you're forcing Num to be an Int which doesn't have a Monoid instance.

However, x = mappend (43 :: Sum Integer) does have a Monoid instance so should compile fine.