jiplucap
jiplucap

Reputation: 164

Why is my definition of signum well-typed?

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

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

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

Related Questions