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