Sunday 27 November 2011

Scala Lazy Types

I have been working on a pricing engine [PE] (a piece of software that calculates bond prices using different models) for the last year, written in Java 6. A PE is essentially a DAG (Directed Acyclic Graph), and there are many ways to implement the relationships between the pricing nodes: remote nodes if you fancy distributed processes, matrix for the parent-child edges, or a pure OO approach where a pricing node has a direct reference to its parents. An example of such a naive implementation would look like this:
public abstract class AbsNode {
  protected List parents = new LinkedList<>();
  protected double price, yield;
  public abstract void calculate();
}

class FutureNode extends AbsNode {
  @Override public void calculate() {
  }
}

class GrossBasisNode extends AbsNode {
  @Override public void calculate() {
    price = parents.get(0).price * 0.98 + 1.0;
  }
}

class BenNode extends AbsNode {
  @Override public void calculate() {
    yield = parents.get(0).yield * 3.14;
  }
}
This obviously is only there to demonstrate the following issues: (1) The code cannot guaranty that the child of a future node must only be a ben node, or that the child of a ben node can only be a ben node, (2) that price and yield should only exist for bonds, and price only for futures (3) you can do pre-check at runtime using instanceof but it is not very nice, (4) there are potential class cast exceptions, (5) there is no compile-time check. Learning Scala, I was wondering if the very much debated type system could come to the rescue. I came up with:
trait MyNode {
  type parentType
  var parents:List[parentType] = List()
  var calcPrice = Double.NaN
  def addParent(parent: parentType) = parents = parent :: parents
}
trait MyBond extends MyNode {
  var calcYield = Double.NaN
}
trait MyFuture extends MyNode
sealed class GenFuture extends MyFuture
sealed class GenBenBond extends MyBond {
  type parentType = MyBond
}
sealed class GenGbnBond extends MyBond {
  type parentType = MyFuture
}
Notice the override of the type parentType: in GenBenBond I enforce the type to be a bond, whereas for the GenGbnBond bond I enforce the type to be MyFuture. This s quite neat. It allows me to write this:
val fut = new GenFuture
val gbn = new GenGbnBond
val ben = new GenBenBond
gbn.addParent(fut)
ben.addParent(gbn)
but this
ben.addParent(fut)
does not compile... exactly what I want. The following amended code shows another good side-effect, the parent in calculate are of the right type:
trait MyNode {
  type parentType
  var parents:List[parentType] = List()
  var calcPrice = Double.NaN
  def addParent(parent: parentType) = parents = parent :: parents
  def calculate = {}
}
trait MyBond extends MyNode {
  var calcYield = Double.NaN
}
trait MyFuture extends MyNode
sealed class GenFuture extends MyFuture
sealed class GenBenBond extends MyBond {
  type parentType = MyBond
  override def calculate = {
    val bond = parents(0)
    bond.calcYield
    bond.calcPrice
  }
}
sealed class GenGbnBond extends MyBond {
  type parentType = MyFuture
  override def calculate = {
    val fut = parents(0)
    // only calcPrice available
    fut.calcPrice 
  }
}

No comments:

Blog Archive