MANOOL
MANOOL is Not an Object-Oriented Language!”

# Lesson 5 — Tutorial

Updated:

## Rational Numbers ADT

In this section we'll discuss a rather long piece of code — taking as an example rational numbers, we'll see how to define your own data types in MANOOL.

This involves studying several orthogonal mechanisms and constructs:

• modules,
• uninterned (unique) symbols,
• concrete object constructors (along with `utils` modules), and
• exception signaling.

Some auxiliary constructs will be exposed (and explained) too:

• `{do ... where ...}`,
• `{scope {...} in ...}`,
• `(...)%`,
• `{unless ... signal ... else ...}`, and
• `{ensure ... in ...}`.

The following example constitutes a working program with a complete “rational number” data type definition and some test code (for ease of reference, line numbers are added as valid comments to the listing):

``````/*001*/  { {extern "manool.org.18/std/0.6/all"} in
/*002*/  : do
/*003*/    { Rationals in -- Test code
/*004*/    : var { A; B } in
/*005*/      A = Rat[1; 2]\$ -- one half rational constant
/*006*/      B = Rat[3; 4]\$ -- three fourth rational constant
/*007*/      Out.WriteLine["A = " A] -- displaying rationals
/*008*/      Out.WriteLine["B = " B]
/*009*/    : for { Op = {array of (+) (-) (*) (/)} } do
/*010*/      Out.WriteLine["A " Op " B = " A.Op[B]] -- rational arithmetic
/*011*/    }
/*012*/    where
/*013*/    Rationals =
/*014*/    { scope { extern } in
/*015*/    : {extern "manool.org.18/std/0.6/all"} in
/*016*/    : let { _Num; _Den } in
/*017*/    : let { IsRat = {{object utils _Num; _Den} in IsInst} } in -- classification
/*018*/    : let
/*019*/      { Gcd = -- Greatest Common Divisor of {Num, Den} - Snippet of code
/*020*/        { if Num == 0 then 1 else
/*021*/        : var { A = Abs[Num]; B = Den } in
/*022*/        : do A after
/*023*/        : while A <> B do: if A > B then A = A - B else B = B - A -- Euclidean algorithm
/*024*/        }'
/*025*/      }
/*026*/      in
/*027*/    : let rec
/*028*/      { Rat = -- Construction
/*029*/        { proc { Num; Den } as
/*030*/        : var { Gcd = Gcd% } in
/*031*/        : object { _Num = Num / Gcd; _Den = Den / Gcd } with
/*032*/          -- Extractors
/*033*/          Num = {proc {A} as A[_Num]@}
/*034*/          Den = {proc {A} as A[_Den]@}
/*035*/          -- Arithmetic operations
/*036*/          (+) =
/*037*/          { proc { A; B } as
/*038*/          : unless IsRat[B] signal TypeMismatch else
/*039*/            Rat[Num[A] * Den[B] + Num[B] * Den[A]; Den[A] * Den[B]]
/*040*/          }
/*041*/          (-) =
/*042*/          { proc { A; B } as
/*043*/          : unless IsRat[B] signal TypeMismatch else
/*044*/            Rat[Num[A] * Den[B] - Num[B] * Den[A]; Den[A] * Den[B]]
/*045*/          }
/*046*/          (*) =
/*047*/          { proc { A; B } as
/*048*/          : unless IsRat[B] signal TypeMismatch else
/*049*/            Rat[Num[A] * Num[B]; Den[A] * Den[B]]
/*050*/          }
/*051*/          (/) =
/*052*/          { proc { A; B } as
/*053*/          : unless IsRat[B] signal TypeMismatch else
/*054*/          : unless Num[B] <> 0 signal DivisionByZero else
/*055*/          : if Num[B] > 0 then
/*056*/            Rat[ Num[A] * Den[B]; Den[A] *  Num[B]]
/*057*/            else
/*058*/            Rat[~Num[A] * Den[B]; Den[A] * ~Num[B]]
/*059*/          }
/*060*/          (~) = Neg -- unary minus (arithmetic negation)
/*061*/          Neg =
/*062*/          { proc { A } as
/*063*/            Rat[~Num[A]; Den[A]]
/*064*/          }
/*065*/          -- Comparison
/*066*/          (==) =
/*067*/          { proc { A; B } as
/*068*/             IsRat[B] & (Num[A] == Num[B]) & (Den[A] == Den[B])
/*069*/          }
/*070*/          (<>) =
/*071*/          { proc { A; B } as
/*072*/            ~IsRat[B] | (Num[A] <> Num[B]) | (Den[A] <> Den[B])
/*073*/          }
/*074*/          (<)  =
/*075*/          { proc { A; B } as
/*076*/          : unless IsRat[B] signal TypeMismatch else
/*077*/            Num[A] * Den[B] <  Num[B] * Den[A]
/*078*/          }
/*079*/          (<=) =
/*080*/          { proc { A; B } as
/*081*/          : unless IsRat[B] signal TypeMismatch else
/*082*/            Num[A] * Den[B] <= Num[B] * Den[A]
/*083*/          }
/*084*/          (>)  =
/*085*/          { proc { A; B } as
/*086*/          : unless IsRat[B] signal TypeMismatch else
/*087*/            Num[A] * Den[B] >  Num[B] * Den[A]
/*088*/          }
/*089*/          (>=) =
/*090*/          { proc { A; B } as
/*091*/          : unless IsRat[B] signal TypeMismatch else
/*092*/            Num[A] * Den[B] >= Num[B] * Den[A]
/*093*/          }
/*094*/          Order =
/*095*/          { proc { A; B } as
/*096*/          : unless IsRat[B] signal TypeMismatch else
/*097*/            Order[Num[A] * Den[B]; Num[B] * Den[A]]
/*098*/          }
/*099*/          -- Conversions and cloning
/*100*/          Str   = {proc {A} as Str[Num[A]] + "/" + Str[Den[A]]}
/*101*/          Int   = {proc {A} as Num[A] / Den[A]}
/*102*/          Clone = {proc {A} as Rat[Num[A]; Den[A]]}
/*103*/        } -- proc
/*104*/      }
/*105*/      in
/*106*/    : ensure Rat[1; 2] + Rat[ 3; 4] == Rat[ 5; 4] in -- unit tests
/*107*/    : ensure Rat[1; 2] - Rat[ 3; 4] == Rat[~1; 4] in
/*108*/    : ensure Rat[1; 2] * Rat[ 3; 4] == Rat[ 3; 8] in
/*109*/    : ensure Rat[1; 2] / Rat[ 3; 4] == Rat[ 2; 3] in
/*110*/    : ensure Rat[1; 2] / Rat[~3; 4] == Rat[~2; 3] in
/*111*/    : ensure Rat[1; 2] < Rat[ 3; 4] in
/*112*/    : export
/*113*/      Rat = -- Construction
/*114*/      { proc { Num; Den } as
/*115*/      : unless IsI48[Num] & IsI48[Den] signal TypeMismatch else
/*116*/      : unless Den <> 0 signal {if Num <> 0 then DivisionByZero else Undefined} else
/*117*/      : if Den > 0 then Rat[Num; Den] else Rat[~Num; ~Den]
/*118*/      }
/*119*/      IsRat = IsRat -- classification
/*120*/    } -- scope
/*121*/  }
``````

Output:

``````A = 1/2
B = 3/4
A + B = 5/4
A - B = -1/4
A * B = 3/8
A / B = 2/3
``````

Caution!!! Work in progress!!!

Updated: