Peeping cat

Scala’s implicit parameters are an extremely powerful beast. They encode Haskell’s type-classes and instances, and by the virtue of being first-class values, enable some really cool tricks. We will see one such trick today.

Failure in implicit resolution is a compile time failure. Once in a while, you may encounter a use case, where you might want to say, “If the implicit is available, do X. Else do Y.”

We can do this with following little data type and implicit provider:

case class Perhaps[E](value: Option[E]) {
  def fold[F](ifAbsent: => F)(ifPresent: E => F): F = {
    value.fold(ifAbsent)(ifPresent)
  }
}

implicit def perhaps[E](implicit ev: E = null): Perhaps[E] = {
  Perhaps(Option(ev))
}

Perhaps is fully isomorphic to Option. Then why bother creating a new type at all? It’s because we do not want to introduce into scope implicit instances of a very commonly used type such as Option.

The implicit provider defined combines Scala’s implicits and default arguments in a creative way to meet our end.

Sample use:

def convertToJson[A](a: A)(implicit pf: Perhaps[Format[A]]): JsValue = {
  pf.fold[JsValue] {
    println("oops")
    JsNull
  } { implicit ev =>
    Json.toJson(a)
  }
}​​

​(I originally posted this to this thread on stackoverflow. Copying it here because I find it worthy of sharing more.)