HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Module representing rational numbers

Submitted by: @import:stackexchange-codereview··
0
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:

  • Is this idiomatic Haskell?



  • Besides Num, Eq, Ord, Show, and Read, are there other typeclasses I should be implementing? I've got "free-floating" functions toInteger' and rationalDiv. 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.

  • You do not export Rational' from the module so no outside code can make one directly



  • numer and denom will conflict with a similar function name in any other code which imports your module. Perhaps rational'Numer and rational'Denom?



  • See http://www.haskell.org/tutorial/numbers.html for a good intro to numbers in Haskell. You missed the Fractional class which defines div, recip, and fromRational.



  • Why are rationalSub and rationalDiv 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 the Rational' type and then you actually return 0 or 1. Be consistent and return Rational' 0 1 and Rational' 1 1



  • rationalFromInteger could be rationalFromInteger = (flip Rational') 1



-
I would define reduce like this

reduce (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.