\$\begingroup\$
\$\endgroup\$
Taking inspiration from SICP Exercise 2.1, I decided to implement a module in Haskell for rational numbers.
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), but
- the Num typeclass requires it to be the same type as the input. -}
rationalSignum :: Rational' -> Rational'
rationalSignum (Rational' n d)
| n == 0 = 0
| (n > 0) == (d > 0) = 1
| otherwise = Rational' (-1) 1
rationalFromInteger :: Integer -> Rational'
rationalFromInteger n = Rational' n 1
{- Truncates towards 0 -}
toInteger' :: Rational' -> Integer
toInteger' (Rational' n d) = n `div` d
rationalEq :: Rational' -> Rational' -> Bool
a `rationalEq` b = (a `rationalCompare` b) == EQ
rationalCompare :: Rational' -> Rational' -> Ordering
a `rationalCompare` b
| (ad == 0) = divByZero a
| (bd == 0) = divByZero b
| (signum ad) == (signum bd) = (an * bd) `compare` (bn * ad)
| otherwise = (bn * ad) `compare` (an * bd)
where (Rational' an ad) = a
(Rational' bn bd) = b
rationalShow :: Rational' -> [Char]
rationalShow a = (show $ numer a) ++ "/" ++ (show $ denom a)
rationalRead :: Int -> [Char] -> [(Rational', [Char])]
rationalRead _ r = [(Rational' (read ns) (read ds), "")]
where (ns, (_:ds)) = break (=='/') r
reduce :: Rational' -> Rational'
reduce a = Rational' (n `div` common) (d' `div` common)
where (Rational' n d) = a
d'
| (d == 0) = divByZero n
| otherwise = d
common = (gcd n d') * (signum d')
asked May 16, 2014 at 6:05
1 Answer 1
\$\begingroup\$
\$\endgroup\$
Not too bad.
- You do not export Rational' from the module so no outside code can make one directly
numer
anddenom
will conflict with a similar function name in any other code which imports your module. Perhapsrational'Numer
andrational'Denom
?- See http://www.haskell.org/tutorial/numbers.html for a good intro to numbers in Haskell. You missed the
Fractional
class which definesdiv
,recip
, andfromRational
. - Why are
rationalSub
andrationalDiv
not defined like add and mult? Be consistent. - In
rationalSignum
your 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 1
andRational' 1 1
rationalFromInteger
could 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)
200_success
145k22 gold badges190 silver badges478 bronze badges
answered May 16, 2014 at 18:00
Explore related questions
See similar questions with these tags.
lang-hs