patternMinor
Module representing rational numbers
Viewed 0 times
numbersmodulerepresentingrational
Problem
Taking inspiration from SICP Exercise 2.1, I decided to implement a module in Haskell for rational numbers.
Some of my concerns include:
where bn = numer b
bd = denom b
rationalAbs :: Rational' -> Rational'
rationalAbs (Rational' n d) = reduce $ Rational' (abs n) (abs d)
{- You might expect the result of signum to be an Int (namely -1 | 0 | 1
Some of my concerns include:
- Is this idiomatic Haskell?
- Besides
Num,Eq,Ord,Show, andRead, are there other typeclasses I should be implementing? I've got "free-floating" functionstoInteger'andrationalDiv. Is there some typeclass that would accommodate them?
- Is this a good way to handle division by zero?
- How is the code layout and organization?
module Rational'
( toInteger'
, rationalDiv
) where
data Rational' = Rational' { numer :: Integer
, denom :: Integer
}
instance Num Rational' where
(+) = rationalAdd
(-) = rationalSub
(*) = rationalMul
abs = rationalAbs
signum = rationalSignum
fromInteger = rationalFromInteger
instance Eq Rational' where
(==) = rationalEq
instance Ord Rational' where
compare = rationalCompare
instance Show Rational' where
show = rationalShow
instance Read Rational' where
readsPrec = rationalRead
divByZero n = error ("Division by zero: " ++ (show n) ++ "/0")
rationalAdd :: Rational' -> Rational' -> Rational'
a rationalAdd b = reduce $ Rational' (an bd + bn ad) (ad * bd)
where (Rational' an ad) = a
(Rational' bn bd) = b
rationalSub :: Rational' -> Rational' -> Rational'
a rationalSub b = a + (Rational' (-bn) bd)
where bn = numer b
bd = denom b
rationalMul :: Rational' -> Rational' -> Rational'
a rationalMul b = reduce $ Rational' (an bn) (ad bd)
where (Rational' an ad) = a
(Rational' bn bd) = b
rationalDiv :: Rational' -> Rational' -> Rational'
a rationalDiv` b = a * (Rational' bd bn)where bn = numer b
bd = denom b
rationalAbs :: Rational' -> Rational'
rationalAbs (Rational' n d) = reduce $ Rational' (abs n) (abs d)
{- You might expect the result of signum to be an Int (namely -1 | 0 | 1
Solution
Not too bad.
-
I would define
- You do not export Rational' from the module so no outside code can make one directly
numeranddenomwill conflict with a similar function name in any other code which imports your module. Perhapsrational'Numerandrational'Denom?
- See http://www.haskell.org/tutorial/numbers.html for a good intro to numbers in Haskell. You missed the
Fractionalclass which definesdiv,recip, andfromRational.
- Why are
rationalSubandrationalDivnot defined like add and mult? Be consistent.
- In
rationalSignumyour comment says the reader would expect a 0 or 1 Int but the return needs to be of theRational'type and then you actually return 0 or 1. Be consistent and returnRational' 0 1andRational' 1 1
rationalFromIntegercould berationalFromInteger = (flip Rational') 1
-
I would define
reduce like thisreduce (Rational' n 0) = divByZero n
reduce (Rational' n d) = Rational' (n `div` common) (d `div` common)
where common = (gcd n d) * (signum d)Code Snippets
reduce (Rational' n 0) = divByZero n
reduce (Rational' n d) = Rational' (n `div` common) (d `div` common)
where common = (gcd n d) * (signum d)Context
StackExchange Code Review Q#49907, answer score: 2
Revisions (0)
No revisions yet.