monnef monnef - 25 days ago 18
Scala Question

Concise conditional operator in Scala

Let me demonstrate what I expect from a conditional operator:

import scalaz._
import Scalaz._
import util.Random

trait Card

class BigCard extends Card

class SmallCard extends Card

object Main extends App {
def printCard(card: Card) {println(card.getClass.getSimpleName)}

// Line bellow works fine, but I would prefer less verbose conditional operator (like ?: from JavaScript).
(if (Random.nextBoolean) new BigCard else new SmallCard) |> printCard

// I thought ScalaZ's ?| was meant to be an alternative to if. But following statement fails to compile.
(Random.nextBoolean ? new BigCard | new SmallCard) |> printCard
}


Result:

[error] xxx/Main.scala:15: type mismatch;
[error] found : SmallCard
[error] required: BigCard
[error] (Random.nextBoolean ? new BigCard | new SmallCard) |> printCard
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed


Is there some alternative to ScalaZ's ?| operator which supports subclasses (not sure about the term, or is it type widening)?

I am looking for a concise conditional operator (so manually adding types is out of question, it would end up being much longer and uglier than
if
). Can it be easily added (as in without custom macro) or does some library supplies such operator?

Answer

Yes, it's quite easy (implicit class can't be declared at top level, put this into an object and import its contents):

class Cond[A](x: Boolean, value: A) {
  def |[B >: A](other: B) = if (x) value else other
}

implicit class CondOp(x: Boolean) {
  def ?[A](y: A) = new Cond(x, y)
}

Scalaz apparently defines def |(other: A) instead.

The drawback (and presumably why Scalaz doesn't do it) is that it also compiles for unrelated types, since in Scala any two types have a common supertype: Random.nextBoolean ? 1 | "". Also, for x ? 1 | 1.0 you won't get a widening to Double, as you would for if; it returns AnyVal instead.

Comments