Let's implement a data type like `Data.Ratio`

of `Haskell`

in Standard ML that will handle rational numbers (with greater precision than floating point).

## signature

```
signature RATIO =
sig
type ratio = int * int
exception DivideByZero
val fromIntPair : int * int -> ratio
val + : ratio * ratio -> ratio
val - : ratio * ratio -> ratio
val * : ratio * ratio -> ratio
val / : ratio * ratio -> ratio
val > : ratio * ratio -> bool
val < : ratio * ratio -> bool
end
```

The name of data type is `ratio`

. It is actually a couple of integer.

Prepare conversion functions `fromIntPair`

, addition, subtraction, multiplication and division of fractions, and comparison operations.

In practice this is a couple of integer, so use `=`

for the equal sign.

## structure

```
structure Ratio : RATIO =
struct
type ratio = int * int
exception DivideByZero
fun fromIntPair (num, 0) = raise DivideByZero
| fromIntPair (0, den) = (0, 1)
| fromIntPair (num, den) =
let fun gcd (x, y) = if x = y then x
else if x > y then gcd (x - y, y)
else gcd (x, y - x)
val g = if den > 0 then gcd (abs num, abs den)
else ~(gcd (abs num, abs den))
in (num div g, den div g)
end
fun (x, y) + (z, w) = fromIntPair (Int.+(Int.*(w, x), Int.*(y, z)), Int.*(y, w))
fun (x, y) - (z, w) = fromIntPair (Int.-(Int.*(w, x), Int.*(y, z)), Int.*(y, w))
fun (x, y) * (z, w) = fromIntPair (Int.*(x, z), Int.*(y, w))
fun (x, y) / (z, w) = fromIntPair (Int.*(x, w), Int.*(y, z))
fun (x, y) > (z, w) = Int.>(Int.*(w, x), Int.*(y, z))
fun (x, y) < (z, w) = Int.<(Int.*(w, x), Int.*(y, z))
end
```

The argument to `fromIntPair`

will call an exception if the denominator is 0, and return `(0, 1)`

if the numerator is 0.

Otherwise, the greatest common divisor is obtained by taking the absolute value and dividing by the greatest common divisor obtained by the numerator and the denominator.

If the denominator is negative, the sign is reversed because only the numerator is signed.

In the four arithmetic operations of fractions, the numerator is calculated with the denominator aligned and then reduced with `fromIntPair`

.

In comparison operations, the numerators are compared after the denominators are aligned.

## example

Use `open`

to expand a structure, and use `local`

to avoid hiding the constraints of the original four operations and comparisons.

```
- local open Ratio
= in
= val twoThird =
= let val oneThird = fromIntPair (1, 3)
= in oneThird + oneThird
= end
= end
= ;
val twoThird = (2,3) : int * int
```

## Top comments (0)