 # How to write a general math operator

#1

Hello. Here’s a problem I cannot solve:
Out of fun I’m writing a library to play with numbers, specifically Rationals:

``````package loonies

final case class Rational(
val numerator: Long,
val denominator: Long)
extends Number {
require(denominator != 0, "The denominator cannot be zero")
val value = (numerator, denominator)

def +(rhs: Rational) =
Rational((numerator * rhs.denominator + denominator * rhs.numerator),
(denominator * rhs.denominator))
def +(rhs: Integer) =
Rational(numerator + rhs.value._1 * denominator, denominator)

override def toString: String = s"Q{\${numerator}/\$denominator}"
}
``````

Integers:

``````package loonies

final case class Integer(val numero: Long) extends Number {
val value = (numero, 1L)
//val num = numero

def +(rhs: Integer): Integer = new Integer(numero + rhs.numero)
def +(rhs: Rational): Rational =
Rational(value._1 * rhs.denominator + rhs.numerator, rhs.denominator)

def -(rhs: Integer): Integer = new Integer(numero - rhs.numero)

def *(rhs: Integer): Integer = new Integer(numero * rhs.numero)

/**
* This operator tests if `this` Integer '''divides''' `that`
* Integer
*/
def |(rhs: Integer): Boolean = rhs.numero match {
case s if (s % numero == 0) => true
case _                      => false
}

override def toString: String = s"Z{\${numero}}"
}
``````

and, finally, the Supertype Number:

``````package loonies

trait Number {
import Number._
def value: (Long, Long)

/**
* This operator should return the result of the division of two integers `a'
* and `b` if `b` divides `a`, otherwise a Rational number with `a` as
* numerator and `b` as denominator
*/
def /(that: Number): Number = that match {
case t: Integer =>
this match {
case s: Integer if t | s    => num(s.numero / t.numero)
case s: Integer if !(t | s) => num(s.numero, t.numero)
}
}
}

object Number {
def num(l: Long) = Integer(l)

def num(
n: Long,
d: Long
) = new Rational(n, d)
}
``````

Now, my focus is on the `/` operator of the latter class: given two Integers `a` and `b`, if `b|a` I’ll get another Integer `c = a ÷ b` otherwise a Rational with `a` as its numerator and `b` its denominator.

So if I use them I get:

``````scala> import loonies._
import loonies._

scala> val a = Integer(5)
a: loonies.Integer = Z{5}

scala> val b = Integer(15)
b: loonies.Integer = Z{15}

scala> val c = b / a
c: loonies.Number = Z{3}

scala> val q = a / b
q: loonies.Number = Q{5/15}

scala> c + q
^
error: type mismatch;
found   : loonies.Number
required: String

scala> q + c
^
error: type mismatch;
found   : loonies.Number
required: String

scala> val s = c + q
^
error: type mismatch;
found   : loonies.Number
required: String
``````

So, it looks like that, even if Rationals and Integers have operators overloaded so to deal with each other, still, I would have to write nested methods to allow Number to tackle the situation.
Anyone with any idea on how to circumvent, if possible, the problem?
Thanks
Guido

#2

The problem is, that your division method has to return `Number`, but all operations are only defined on the subclasses. As the compiler cannot know which of both subtypes `c` will be, it cannot decide which `+` function to call. Also, the signatures in both number types are incompatible (different return types).

A simple solution would be to add the signature for the operations you want to the `Number` trait, e.g.

``````trait Number {
def +(rhs: Number): Number
// ...
}
``````

and then implement it on both subtypes via a pattern match, merging the currently two methods:

``````final case class Integer(numero: Long) extends Number {

def +(rhs: Number): Number = rhs match {
case Integer(num) => Integer(numero + num)
case Rational(numer, denom) => Rational(value._1 * denom + numer, denom)
}
// ...
}
``````

This has the downside, that if you add another `Number` subtype, a runtime error may occur when passing that to one of those operators. A common solution is to put the types into the same file and make `Number` a sealed trait, so that no new types can be defined outside that file and allowing the compiler to do exhaustiveness checks on pattern matches.

A completely different approach would be to use typeclasses instead of subtyping. You can have a look at the source of https://typelevel.org/spire/ for a math library using that approach (although it does not wrap the primitive numeric types, and so cannot change to rational automatically for dividing non-divisible integers)

1 Like
#3

Thanks! It works.
Yeah, I was afraid I would have ended up descending the path of Type Classes, which, given my level of expertise, I’m afraid, will present a steep gradient.
Thanks again.

#4

To be clear, you only need type classes if you want to add new operations to scala.Int etc. If you only work with your own types, inheritance is fine.

1 Like