8

I am currently working on a project with Scala and it seems I don't fully understand Scala's Typesystem :-/

I have the following situation:

def reviews(id: Int) = Action { implicit request =>
  Ok(html.products.reviews(
    reviewlist,
    reviewlist
      .find(review => review.id == id)
      .getOrElse(reviewlist.headOption)
  ))
}

Unfortunately the compiler says, he cannot convert Product to Option[Review], so I changed the code

reviewlist
  .find(review => review.id == id)
  .getOrElse(reviewlist.headOption)

with

id match {
  case 0 => reviewlist.headOption
  case id => reviewlist.find(review => review.id == id)
}

which seems to work now, even though its not exactly the same thing as it does, for example, not show the first record anymore if an invalid review id is being submitted. it will then pretend, that there are no reviews available yet.

I then broke the problem down to a veeery simple sample:

val a: Option[Int] = Some(1).getOrElse(Some(1))

So, has anyone an idea, why the expression on the right side is not of the type Option[Int]?? Both, Some(1) and None inherit directly from Option and this expression is actually Some(1) in any or am I wrong?

Interestinly enough

val a: Option[Int] = None.getOrElse(None)

works, but all other combinations do not...

Silverdust
  • 1,503
  • 14
  • 26

3 Answers3

14

You wanted:

val a: Option[Int] = Some(1).orElse(Some(1))

Because

x.getOrElse(y)

will return 1 if x is Some(1) or y (which is Some(1)) if x is None, or speaking in code:

if (Some(1).isDefined) 1 else Some(1)
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
  • 1
    `x.orElse(Some(y))` is always silly for any `x` and `y`. You want `x.getOrElse(y)`. – Tom Crockett Jul 09 '13 at 20:56
  • I'm not saying `orElse` is silly, I'm saying that passing it a value explicitly wrapped in `Some` is silly. – Tom Crockett Jul 09 '13 at 21:00
  • 1
    @pelotom - What if you want to pass it to something that requires an `Option`? – Rex Kerr Jul 09 '13 at 21:39
  • @pelotom, thats exactly what I do ;-) the reviews view template requires an Option[Review]. The code written is this: find the first element whose id matches the parametric id (There is probably no such element, thus Option). If it doesn't find any however, then take the first element from the list instead (but again, this might not be possible because the list might be empty). So by the end of the day, the view must handle the case when there is simply no review to render, thus the function takes Option[Review] and .orElse is not silly but genius in my opinion! – Silverdust Jul 09 '13 at 22:50
  • For the case where an `Option` is required, I think it's clearer to say `Some(x.getOrElse(y))`, to make it clear that the result is total but is being lifted into partiality. But that's a matter of taste. – Tom Crockett Jul 09 '13 at 23:16
  • @pelotom it is hardly applicable to the particular OP's case – om-nom-nom Jul 09 '13 at 23:18
  • @om-nom-nom you're right, I was reacting specifically to seeing `Some(1).orElse(Some(1))`, which was just a contrived example. I retract my criticism. – Tom Crockett Jul 09 '13 at 23:28
9

The type signature of Option.getOrElse is

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

That means that when you call getOrElse on an Option[A], it is going to try to return you something of type A. If the type of default (B) isn't the same as A, it is going to look for the closest shared ancestor of A and B. In your case, A and B are Option[Int] and Int. The best the compiler can do is Any.

brandon
  • 675
  • 6
  • 10
  • nice one, +1--gets to the heart of the matter but without any problem-specific detail. – doug Feb 08 '16 at 21:10
1

An Option value has two representations, a good value (Some(...)) or a bad value (None).

With getOrElse you can reduce an Option to the type contained by it. Imagine it as being a process of unpacking the value, removing it from the container.

In this process of unpacking, the Option is stripped and only the type contained by it is returned, so in your example you really have this:

val a int = Some(1).getOrElse(2) // 1

And to do what you want is:

val b Option[Int] = Some(1).orElse(Some(2)) // Some(1)
senia
  • 37,745
  • 4
  • 88
  • 129
nemo
  • 55,207
  • 13
  • 135
  • 135