Reputation: 164
I did the following class instance of Num:
newtype Natural a = Nat a deriving Show
toNatural :: (Integral a) => a -> (Natural a)
toNatural x | x < 0 = error "Negative"
| otherwise = Nat x
fromNatural :: (Integral a) => (Natural a) -> a
fromNatural (Nat i) = i
instance (Integral a) => Num (Natural a) where
x + y = toNatural((fromNatural x) + (fromNatural y))
x - y = let r = fromNatural x - fromNatural y
in if r < 0 then error "Negative"
else toNatural r
x * y = toNatural ((fromNatural x) * (fromNatural y))
abs x = x
signum x | (fromNatural x) == 0 = 0 --by error, I wrote 0 instead of (Nat 0)
| otherwise = 1 --by error, I wrote 1 instead of (Nat 1)
fromInteger = toNatural . fromIntegral
I am surprised that signum doesn't give a type error, and moreover signum (Nat 5) gives (Nat 1) instead of 1. I guess that an implicit coercion is applied, but I wonder why. Please, could someone explain this issue?
Upvotes: 3
Views: 102
Reputation: 477338
Integer literals like 0
, 5
, 24
, (-3)
have type:
Prelude> :t 0
0 :: Num t => t
An integer literal can thus be converted to any type that is an instance of Num
. If you write 0
, then implicitly you write fromInteger :: Num a => Integer -> a
in front.
Since you thus define a Num
instance for Natural a
, an integer literal like 0
and 1
can have type Integral a => Natural a
as well. Or as described in the documentation for fromInteger
:
Conversion from an Integer. An integer literal represents the application of the function
fromInteger
to the appropriate value of type Integer, so such literals have type(Num a) => a
.
In a similar way you can make a type an instance of Fractional
and floating point literals represent the application of fromRational :: Fractional a => Rational -> a
, and with the OverloadedStrings
extension [ghc-doc] you can make it an instance of IsString
for string literals. These represent than an application of fromString :: IsString a => String -> a
.
Upvotes: 6