Home > Mobile >  Why does the expression `foldr (mappend . Sum) 1 [2]` type checks?
Why does the expression `foldr (mappend . Sum) 1 [2]` type checks?

Time:11-21

If I beta-reduce the following expression:

  foldr (mappend . Sum) 1 [2]
= (mappend . Sum) 2 (foldr (mappend . Sum) 1 [])
= (mappend . Sum) 2 1
= mappend (Sum 2) 1
...

Looking at the type of:

// mappend (<>) :: Monoid a => a -> a -> a

We can see the last line has a type error, because the constant 1 should belong to Monoid class (and it does not).

However, ghci doesn't complain.

Why does that expression type checks ?

CodePudding user response:

Short answer: 1 is interpreted as a Sum a, so the type of your foldr is:

foldr (mappend . Sum) 1 [2] :: Num a => Sum a

where 2 has type a and the 1 has type Sum a.

Sum a is an instance of Num if a is an instance of Num, indeed, the source code says [src]:

newtype Sum a = Sum { getSum :: a }
        deriving ( Eq       -- ^ @since 2.01
                 , Ord      -- ^ @since 2.01
                 , Read     -- ^ @since 2.01
                 , Show     -- ^ @since 2.01
                 , Bounded  -- ^ @since 2.01
                 , Generic  -- ^ @since 4.7.0.0
                 , Generic1 -- ^ @since 4.7.0.0
                 , Num      -- ^ @since 4.7.0.0
                 )

This thus means that if you write an integer literal, like 1, it can be constructed as a Sum a for any a that is an instance of Num, so 1 :: Sum Integer is Sum 1.

This thus means that the 1 in your foldr has type Sum a, and thus for example:

   mappend (Sum 2 :: Sum Integer) (1 :: Sum Integer)
-> Sum (2   1)
-> Sum 3

CodePudding user response:

In the documentation for the Sum type you will find this tell-tale instance:

Num a => Num (Sum a)

(which of course makes perfect sense that it should be there - not only is Sum a "morally the same type" as a, Sum is clearly designed to be used pretty much only with numeric types anyway).

Because numeric literals like 1 in Haskell can be of any type that's an instance of the Num class, there is no type error in mappend (Sum 2) 1 because it is effectively translated into mappend (Sum 2) (Sum 1) automatically by the compiler. (Since in that Num instance for Sum a, fromInteger 1 will be Sum 1.)

  • Related