question about Option[A].getOrElse

602 views
Skip to first unread message

诺铁

unread,
Apr 16, 2013, 11:21:09 AM4/16/13
to scala-user
Hi,

when using Option.getOrElse, I find the following code compiles (I expect it fail to compile)

  scala> val (x,y) = Some((1,2)).getOrElse(3)
  x: Any = 1
  y: Any = 2

well, the api doc says:

 def getOrElse[B >: A](default: ⇒ B)B

so the type B can be different than A, as long as B is super class of A. so compiler give it the type Any. that's understandable.
  scala> val (x,y):Any = Some((1,2)).get
  x: Any = 1 
  y: Any = 2
that's fine,but
scala> val (x,y):Any = None.getOrElse(3)
scala.MatchError: 3 (of class java.lang.Integer)

so when the else path is evaluated, a runtime exception is thrown.

I find this in my production code when doing codereview. something like:
val (pid, pname) = productDetail.map(p=>(p.id, p.name)).getOrElse("")
I meant to write getOrElse((0,"")) . but made a mistake.  it compiles happily, but surprise me. 

should we have another version of getOrElse
def getOrElse[A](default : =>A): A
this should work as my expectation. or have I missed something?

Luis Ángel Vicente Sánchez

unread,
Apr 16, 2013, 11:30:17 AM4/16/13
to 诺铁, scala-user
Even if scala type inference is very powerful sometimes it make the wrong decisions; i always specify the types for any variable or function definition...

val (pid, pname): (String, String) =  productDetail.map(p=>(p.idp.name)).getOrElse("")

would have catch the error at compile time because right side is of type Any.


2013/4/16 诺铁 <not...@gmail.com>

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Dennis Haupt

unread,
Apr 16, 2013, 11:40:31 AM4/16/13
to "诺铁", scala-user
you are trying:
3 match {case (a,b) => ...}
 
and that doesn't work
 
Gesendet: Dienstag, 16. April 2013 um 17:21 Uhr
Von: "诺铁" <not...@gmail.com>
An: scala-user <scala...@googlegroups.com>
Betreff: [scala-user] question about Option[A].getOrElse
--

Derek Williams

unread,
Apr 16, 2013, 11:41:10 AM4/16/13
to 诺铁, scala-user
If you add the expected type to the pattern match you should get a compile error:

scala> val (a, b) = "foo": Any
scala.MatchError: foo (of class java.lang.String)

scala> val (a, b): (String, String) = "foo": Any
<console>:7: error: type mismatch;
 found   : Any
 required: (String, String)
       val (a, b): (String, String) = "foo": Any
                                           ^

If you'd rather have the alternate getOrElse, I'd suggest adding it to Option with an implicit class:

implicit class OptionSafe[A](val opt: Option[A]) extends AnyVal {
  def getOrElseSafe(default: => A): A = opt getOrElse default
}



--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Derek Williams

诺铁

unread,
Apr 16, 2013, 8:24:04 PM4/16/13
to Derek Williams, scala-user
specify the expected type and the getOrElseSafe are both good idea,thanks.

Som Snytt

unread,
Apr 17, 2013, 12:59:00 AM4/17/13
to Derek Williams, 诺铁, scala-user
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.
Reply all
Reply to author
Forward
0 new messages