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.)
