Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is Ratio implemented in Haskell?

Tags:

haskell

This is something I have been confused about for a while and I am not sure how I can learn more about it. Let's say I have the following program:

main :: IO ()
main = do
    x <- liftM read getLine
    y <- liftM read getLine
    print (x % y)

If I run this with the input 6 and 2, it will print 3 % 1.

At what point does the simplification happen (namely the division by the gcd)? Is it implemented in show? If so, then is the underlying representation of the rational still 6 % 2? If not, then does (%) do the simplification? I was under the impression that (%) is a data constructor, so how would a data constructor do anything more than "construct"? More importantly, how would I actually go about doing similar things with my own data constructors?

I appreciate any help on the topic.

like image 647
Emil Avatar asked Mar 25 '14 14:03

Emil


2 Answers

Ratio is actually implemented in GHC.Real (on GHC, obviously), and is defined as

data Ratio a = !a :% !a deriving (Eq)

The bangs are just there for strictness. As you can see, the function % is not a data constructor, but :% is. Since you aren't supposed to construct a Ratio directly, you use the % function, which calls reduce.

reduce ::  (Integral a) => a -> a -> Ratio a
{-# SPECIALISE reduce :: Integer -> Integer -> Rational #-}
reduce _ 0              =  ratioZeroDenominatorError
reduce x y              =  (x `quot` d) :% (y `quot` d)
                           where d = gcd x y
(%) :: (Integral a) => a -> a -> Ratio a
x % y =  reduce (x * signum y) (abs y)

The rule is that if an operator starts with a colon :, then it is a constructor, otherwise it is just a normal operator. In fact, this is part of the Haskell standard, all type operators must have a colon as their first character.

like image 136
bheklilr Avatar answered Sep 27 '22 17:09

bheklilr


You can just look at the source to see for yourself:

instance  (Integral a)  => Num (Ratio a)  where
    (x:%y) + (x':%y')   =  reduce (x*y' + x'*y) (y*y')
    (x:%y) - (x':%y')   =  reduce (x*y' - x'*y) (y*y')
    (x:%y) * (x':%y')   =  reduce (x * x') (y * y')
    negate (x:%y)       =  (-x) :% y
    abs (x:%y)          =  abs x :% y
    signum (x:%_)       =  signum x :% 1
    fromInteger x       =  fromInteger x :% 1 

reduce ::  (Integral a) => a -> a -> Ratio a
reduce _ 0              =  ratioZeroDenominatorError
reduce x y              =  (x `quot` d) :% (y `quot` d)
                           where d = gcd x y
like image 39
Thomas M. DuBuisson Avatar answered Sep 27 '22 18:09

Thomas M. DuBuisson