views:

199

answers:

2

I'm writing a code generator which produces Scala output.

I need to emulate a ternary operator in such a way that the tokens leading up to '?' remain intact.

e.g. convert the expression c ? p : q to c something. The simple if(c) p else q fails my criteria, as it requires putting if( before c.

My first attempt (still using c/p/q as above) is

c match { case(true) => p; case _ => q }

another option I found was:

class ternary(val g: Boolean => Any) { def |: (b:Boolean) = g(b) }

implicit def autoTernary (g: Boolean => Any): ternary = new ternary(g)

which allows me to write:

c |: { b: Boolean => if(b) p else q }

I like the overall look of the second option, but is there a way to make it less verbose?

Thanks

+5  A: 

Even though the syntax doesn't evaluate in the expected order--it binds the conditional to the first option!--you can make your own ternary operator like this:

class IfTrue[A](b: => Boolean, t: => A) { def |(f: => A) = if (b) t else f }
class MakeIfTrue(b: => Boolean) { def ?[A](t: => A) = new IfTrue[A](b,t) }
implicit def autoMakeIfTrue(b: => Boolean) = new MakeIfTrue(b)

The trick is to interpret ? as a method on a MakeIfTrue object that binds the condition to the object to return in the "true" case. The resulting IfTrue object now uses the | method as a request to evaluate the condition, returning the stored true option if the condition is true, or the just-passed-in one if it's false.

Note that I've used stuff like => A instead of just A--by-name parameters--in order to not evaluate the expression unless it's actually used. Thus, you'll only evaluate the side that you actually need (just like an if statement).

Let's see it in action:

scala> List(1,3,2).isEmpty ? "Empty" | "Nonempty"
res0: java.lang.String = Nonempty

scala> (4*4 > 14) ? true | false
res1: Boolean = true

scala> class Scream(s: String) { println(s.toUpperCase + "!!!!") }
defined class Scream

scala> true ? new Scream("true") | new Scream("false")
TRUE!!!!
res3: Scream = Scream@1ccbdf7

(P.S. To avoid confusion with the Actor library ?, you probably ought to call it something else like |?.)

Rex Kerr
Doh, too slow. I was in the progress of typing something similar to this up, only to get the dreaded "2 new answers" message
Jackson Davis
@Jackson: Interesting alternatives are still worthwhile!
Rex Kerr
Nice! Only thing I'll change is ? to |? to get the lowest operator priority.
Alex R
@Alex: Just keep in mind that this has a lot more overhead than an if-statement. Performance-critical code should still use ifs (which are ~40x faster in my microbenchmarks--but it's ~40 ns vs. ~1 ns on a modern 3GHzish machine, so you'd really need to be doing this many millions of times per second before you'd care).
Rex Kerr
+3  A: 

You could use something like this

sealed trait TernaryOperand[A] {
  def >(q: => A): A
}

case class TernarySecond[A](val p: A) extends TernaryOperand[A] {
  def >(q: => A) = p
}

case class TernaryThird[A]() extends TernaryOperand[A] {
  def >(q: => A) = q
}

implicit def ternary(c: Boolean) = new {
  def ?[A](p: => A): TernaryOperand[A] = if (c) TernarySecond(p) else TernaryThird()
}

val s1 = true ? "a" > "b"
println(s1) //will print "a"

val s2 = false ? "a" > "b"
println(s2) //will print "b"

This code converts any boolean value to an anonymous type that has a method called ?. Depending on the value of the boolean, this method will either return TernarySecond or TernaryThird. They both have a method called > which returns the second operand or the third one respectively.

Michel Krämer