0

I'm going through the course Functional Programming Principles in Scala on Coursera. There is an example of implementation of the Peano numbers which goes like this:

abstract class Nat {
  def isZero: Boolean
  def predecessor: Nat
  def successor: Nat = new Succ(this)
  def +(that: Nat): Nat
  def -(that: Nat): Nat
}

object Zero extends Nat {
  def isZero = true
  def predecessor: Nat = throw new Error("0.predecessor")
  def +(that: Nat): Nat = that
  def -(that: Nat): Nat = if (that.isZero) this else throw new Error("negative number")
}

class Succ(n: Nat) extends Nat {
  def isZero: Boolean = false
  def predecessor: Nat = n
  def +(that: Nat): Nat = new Succ(n + that)
  def -(that: Nat): Nat = if (that.isZero) this else n - that.predecessor
}

I wrote couple of unit tests. Most of the pass, but the following ones - written in naive way - are failing from obvious reasons (comparison of different instances):

trait Fixture {
  val one = new Succ(Zero)
  val two = new Succ(one)
}

test("successor of zero is one") {
  new Fixture {
    assert(Zero.successor == one)
  }
}

test("successor of one is two") {
  new Fixture {
    assert(one.successor == two)
  }
}

test("one plus zero is one") {
  new Fixture {
    assert((one + Zero) === one)
  }
}

test("one plus one is two") {
  new Fixture {
    assert((one + one) === two)
  }
}

My question is: how a unit test should be implemented that successfully tests + and - operations on peano numbers?

Just in case, here you can find remaining unit tests.

Vít Kotačka
  • 1,472
  • 1
  • 15
  • 40
  • How would you actually use this class? Can you not write an equality operator? With the current operations, you could always check, for example, that the predecessor of one has no predecessor and awkwardly chain things that way. –  May 15 '17 at 12:42
  • 1
    Why is your `Succ` class not a `case class`? It would make everything easier (unless you haven't heard about them yet). – Cyrille Corpet May 15 '17 at 12:46
  • @JETM It's just a demonstration of how values can be objects. I've started to write unit test just to better understand the principle. Yes, that was my primary idea to count how many predecessor are to `Zero`, but I wander if there is something more idiomatic to *Peano numbers*. – Vít Kotačka May 15 '17 at 12:58
  • @CyrilleCorpet The code example is directly from the course, written by Martin Odersky. I'm new to *Scala*, so I haven't met `case class` yet. Thanks for the tip. – Vít Kotačka May 15 '17 at 13:04

2 Answers2

1

Thanks to hint from Cyrille Corpet, it looks to me that it's elegant to use case class which "compares by structure, not by reference". All unit tests are passing now without any change.

case class Succ(n: Nat) extends Nat {
  def isZero: Boolean = false
  def predecessor: Nat = n
  def +(that: Nat): Nat = new Succ(n + that)
  def -(that: Nat): Nat = if (that.isZero) this else n - that.predecessor
}
Community
  • 1
  • 1
Vít Kotačka
  • 1,472
  • 1
  • 15
  • 40
-1

Looking at your tests it appears that you want to test equality conditions, you should write a function for that:

def eq(i: Nat, j: Nat): Boolean =
  if (i.isZero | j.isZero)
    i.isZero == j.isZero
  else
    eq(i.predecessor, j.predecessor)

The replace your === & == by call to eq. You might also consider overriding the equal method instead of having that as external (test) function.

OlivierBlanvillain
  • 7,701
  • 4
  • 32
  • 51