Wednesday, August 17, 2011

Scala DSL technique - if-else constructs

Post moved to http://www.coolscala.com/wiki/Cool_Scala/Scala_DSL_technique_if-else_constructs

1 comments:

Unknown said...

Hi, I needed exactly this, thank you very much. I was trying to write something like that but wrote an elseif_ method - but that way, I couldn't call elseif_ but had to call .elseif_. After some painless Googling, I found your code, and integrated the missing idea into my code (with credit).

Regarding your question, here's my (simplified) version of your code. Note that it is purely functional, and that it prevents statically additional else_ branches, because else_ returns a different type; one could easily build a non-purely functional version using mutable collections, but I don't think it's worth it (not for my use-case anyway). Your code ends up implementing manually a linked list, in practice, and that's something you should avoid, especially in Scala where standard collections have so much to give.

So, here's the code (and apologies for the formatting, but the <pre> tag is forbidden here):

//Simplified code from my framework - similar to the Delite core:
trait Exp[+T] {
def interpret: T
}
implicit def toExp[T](t: T) = Const(t)
case class Const[T](t: T) extends Exp[T] {
def interpret = t
}

//The solution
case class IfThenElse[T](cond: Exp[Boolean], thenBody: Exp[T], elseBody: Exp[T])

case class Elseable[T](conds: Seq[Exp[Boolean]], bodies: Seq[Exp[T]]) {
def else_[U >: T](elseBody: Exp[U]): Exp[U] =
(conds, bodies).zipped.foldRight(elseBody) {
case ((cond, thenBody), curr) => IfThenElse(cond, thenBody, curr)
}
//This overload allows chaining if-else if. The idea comes from:
//http://blog.razie.com/2011/08/scala-dsl-technique-if-else-constructs.html
def else_[U >: T](branch: Elseable[U]) = Elseable(conds ++ branch.conds, bodies ++ branch.bodies)
}
def if_[T](cond: Exp[Boolean])(thenBody: Exp[T]) = Elseable(Seq(cond), Seq(thenBody))