This was a good one. And the safe extension is a great suggestion.
Someone should notify scala-puzzlers. It's very educational.
I also learned to hold code review before my code hits production.
Others have mentioned making sure types are annotated; my middle ground is to use Pair(a,b) so at least I know I'm getting the arity right. I realize it's more hip to use only parens to make and break tuples, but how do you read that when you're doing code review on an Android device on the bus?
Not that this happens every day, but probably like others, I sprinkle in a getOrElse or a recover where the else is an edge case and who knows if I tested it.
val (num, name) = Option(Pair(7, "seven")) getOrElse Pair("eight", 8)
Here's an attempt to apply the ambiguous implicit trick to limit the inference of B to something that might be useful.
It's not entirely happy, but maybe it's not my fault. I don't know why the AnyVal version is happier than the AnyRef version. This is the simplest trick to come out of Chuusai; maybe I'll stay up late tonight and try and grok what I typed in. Maybe a version of something like it ships with shapeless.
// Encoding for "A is a proper subtype of B"
trait <<:<<[A, B]
// Uses ambiguity to rule out the cases we're trying to exclude
implicit def psub[A, B]: A <<:<< B = null
implicit def psubAmbig1[A, B](implicit ev1: A <:< B, ev2: A =:= B) : A <<:<< B = null
implicit def psubAmbig2[A, B](implicit ev1: A <:< B, ev2: A =:= B) : A <<:<< B = null
type Proper[T] = {
type Lambda[U] = U <<:<< T
}
// if the inferred B is AnySomething, refuse to compile
implicit class NarrowElse[A <: AnyVal](val option: Option[A]) {
def getOrElseProperly[B >: A : Proper[AnyVal]#Lambda](b: B): B = option getOrElse b
}
implicit class NarrowElse2[A <: AnyRef](val option: Option[A]) {
def getOrElseProperly0[B >: A : Proper[AnyRef]#Lambda](b: B): B = option getOrElse b
def getOrElseProperly[B](b: B)(implicit ev1: A <:< B, ev2: Proper[AnyRef]#Lambda[B]): B =
(option getOrElse b).asInstanceOf[B] // beggars can't be chuusai
}
Some(3L) getOrElseProperly 5L // ok
Some(3L) getOrElseProperly 5 // no
val (m,n) = Option[(Int,String)](null) getOrElseProperly0 (3) // fails to fail
val (i,j) = Option[(Int,String)](null) getOrElseProperly (3) // not this time
val (num, name) = Option(Pair(7, "seven")) getOrElseProperly Pair("eight", 8)
// it can be useful to recover with a wider type
trait HumanResource {
def id: Int
def name: String
override def toString = s"Employee $id, '${name}'"
}
case class Person(id: Int, name: String) extends HumanResource
val pr = Person(123, "Fred")
val np: Option[Person] = None
val hr = np getOrElseProperly new HumanResource {
override def id = 1
override def name = "Martin"
}
As a footnote, I hope the Scala store puts out a t-shirt that reads, "Beggars can't be Chuusai," I'd wear that one.