Implementing a Ternary Operator in Scala
Tags: scala, java, functional
Published: 2020-06-26 22:09:01

> _Photo by [Magda Ehlers](https://www.pexels.com/@magda-ehlers-pexels?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) from [Pexels](https://www.pexels.com/photo/three-animal-head-pencils-1329308/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)_

---

Scala doesn't have the traditional ternary operator from Java

```java
// java
var x = condition ? ifTrue : ifFalse
```

Instead, ternary-like expressions can be defined using `if`-`else` expressions, which -- unlike in Java -- return a value

```scala
// scala
var x = if (condition) ifTrue else ifFalse
```

_(All code from this point on is Scala code.)_

But this is a bit verbose. It would be nice if we could somehow recreate the simple `?:` notation in Scala. Can it be done? Let's try.

First, we need to think about what this is actually doing. Basically, a ternary operator defines a function which takes three arguments as parameters -- a boolean `condition` and two [by-name](https://docs.scala-lang.org/tour/by-name-parameters.html) parameters, one for each possible value of the `condition`.

A naive implementation could be a function with a signature like

```scala
def myTernary (condition: Boolean, ifTrue: => Any, ifFalse => Any)
```

Although the correct functionality could be implemented, the signature requires `condition`, `ifTrue`, and `ifFalse` to all be passed as arguments to some method, where what we really want is `condition`, followed by a `?`, followed by `ifTrue`, etc.

Instead, we can define a method called `?` on a class `Ternable`, and provide an [implicit conversion](https://docs.scala-lang.org/overviews/core/implicit-classes.html) from `Boolean` to `Ternable`, like

```scala
object Implicits {
  implicit class Ternable (condition: Boolean) {
    def ? (ifTrue: => Any, ifFalse: => Any): Any = {
      if (condition) ifTrue else ifFalse
    }
  }
}
```

This gets us a bit closer, as we can now write code like

```scala
import Implicits._

(3 > 2).?("fine", "uh...") // val res0: Any = fine
(3 < 2).?("what", "yeah")  // val res1: Any = yeah
```

We can't drop the `.` and write

```scala
(3 < 2) ? ("what", "yeah")
```

...though, because that syntactic sugar only works when the function (in this case `?`) takes a single argument. This one takes two.

We also want to add a `:` symbol in between the `ifTrue` and `ifFalse`. Scala's [associativity rules](https://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html) say that any operators ending in `:` are _right-associative_, meaning that the argument on the right-hand side of the `:` -- `ifFalse` -- is the one for which the operator `:` must be defined.

Since `ifFalse` is of type `Any`, we need another implicit conversion to add a `:` method to the `Any` type, but what should the method signature look like?

Because `?` has [a higher precedence](https://docs.scala-lang.org/tour/operators.html) than `:`, the first part of the expression will be evaluated first

```scala
var x = (condition ? ifTrue) : ifFalse
```

So `:` can take a single argument... but what should that argument's type be? `ifTrue` could evaluate to `Any` kind of value, so how can we signal that (1) `condition` was `true`, `ifTrue` was evaluated, and we should return that value vs. (2) `condition` was `false`, `ifTrue` was not evaluated, and we need to evaluate `ifFalse`?

One way is to change the method signature of `?`. We can have it return an `Option[Any]` -- a `Some` in case (1) and a `None` in case (2)

```scala
object Implicits {
  implicit class Ternable (condition: Boolean) {
    def ? (ifTrue: => Any): Option[Any] = {
      if (condition) Some(ifTrue) else None
    }
  }
}
```

Because we've now reduced the [arity](https://en.wikipedia.org/wiki/Arity) of `?` from 2 to 1, we can also make use of the syntactic sugar which lets us drop the `.()` notation for method calls

```scala
import Implicits._

(3 > 2) ? "hey" // val res0: Option[Any] = Some(hey)
(3 < 2) ? "hey" // val res1: Option[Any] = None
```

This means that our `:` method should accept an `Option[Any]` as its argument type

```scala
object Implicits {

  ...

  implicit class Colonable (ifFalse: => Any) {
    def : (intermediate: Option[Any]): Any =
      intermediate match {
        case Some(ifTrue) => ifTrue
        case None => ifFalse
      }
  }
}
```

This _would_ work beautifully... if `:` weren't a part of Scala's basic language syntax. Remember that `:` is used to define the type of an object (as in `val x: String`), so if we try to define the method as above, we get a compiler error ("identifier expected").

Since we want to define an implicit method on `Any` ([which has very few built-in methods](https://www.scala-lang.org/api/current/scala/Any.html)), we can just pick another operator which sort of looks like `:` -- how about `|`? It already means "or" in many contexts, which is more or less what it means here. Remember, though, that we still need the `:` as the last character in the method name to get the right associativity

```scala
object Implicits {

  ...

  implicit class Colonable (ifFalse: => Any) {
    def |: (intermediate: Option[Any]): Any =
      intermediate match {
        case Some(ifTrue) => ifTrue
        case None => ifFalse
      }
  }
}
```

Check it out!

```scala
import Implicits._

(3 > 2) ? "true" |: "false" // val res0: Any = true
(3 < 2) ? "true" |: "false" // val res1: Any = false
```

It works! With syntax _almost as clean_ as in Java. (Never thought I would say that with a straight face.)

How can we improve on this? Well, the return type is currently `Any`, which is less than ideal. Can we infer a narrower type from the types of `ifTrue` and `ifFalse`?

We could use some [type class craziness](https://stackoverflow.com/a/8088459/2925434) to try to find the narrowest common supertype (NCS) of `ifTrue` and `ifFalse`, but for any heterogenous pair of value types ("primitives"), the NCS is `AnyVal`, which is not extremely helpful.

Instead, a more Scala-like solution might be to use an `Either` type

```scala
object Implicits {
  implicit class Ternable (condition: Boolean) {
    def ? [T](ifTrue: => T): Option[T] = {
      if (condition) Some(ifTrue) else None
    }
  }
  implicit class Colonable [T, F](ifFalse: => F) {
    def |: (intermediate: Option[T]): Either[T, F] =
      intermediate match {
        case Some(ifTrue) => Left(ifTrue)
        case None => Right(ifFalse)
      }
  }
}

import Implicits._

((3 > 2) ? "true" |: 42) match {
  case Left(v) => s"$v is a ${v.getClass}"
  case Right(v) => s"$v is a ${v.getClass}"
}

// prints: true is a class java.lang.String

((3 < 2) ? "true" |: false) match {
  case Left(v) => s"$v is a ${v.getClass}"
  case Right(v) => s"$v is a ${v.getClass}"
}

// prints: false is a boolean
```

So there you have it! A pretty close approximation of the ternary operator in Scala, which maintains as much type information as possible, with minimal noise.

Let me know what you think in the comments!