noncom
noncom

Reputation: 4992

Scala - Option.getOrElse() "type erasure"?

I am trying to use the Option.getOrElse() method but it returns etiher Any or ScalaObject instead of an instance of the correct class that the Option was parametrized with. I can't find any mention about this problem and it does not seem like it should be there. What am I doing wrong?

class MyClass {

  def isOk = true

}

val myVal = Some(new MyClass) // :Option[MyClass]

val check = myVal.getOrElse(false).isOk

Can't call the isOk method because it tries calling it upon Any.

Upvotes: 4

Views: 9086

Answers (7)

Dan Burton
Dan Burton

Reputation: 53665

So you've got an Option[A], and a function A => B, and a default B for when the Optional value is None, and you want to end up with a B. (In your case, A is MyClass and B is Boolean).

Being a Haskeller, the first thing I think to do is hoogle. Recall that in Haskell, Option is called Maybe. So we hoogle Maybe a -> (a -> b) -> b -> b, and the top hit is maybe :: b -> (a -> b) -> Maybe a -> b, which does exactly what we want.

data MyClass = MyClass { isOK :: Bool }
newMyClass = MyClass { isOK = true }

myVal = newMyClass
check = maybe False isOK myVal

Well that's well and good, but what about Scala? Well, the Scala equivalent to hoogle is Scalex. I searched Scalex for Option[A] => B => (A => B) => B, but to no avail. So instead, let's check out how the maybe function was implemented in Haskell. You can find the source by following the appropriate links from hoogle.

maybe :: b -> (a -> b) -> Maybe a -> b
maybe n _ Nothing  = n
maybe _ f (Just x) = f x

Seems easy enough to translate into Scala

def option[A, B](opt: Option[A])(n: B)(f: A => B) = opt match {
  case None => n
  case Some(x) => f(x)
}

This can then be used like so:

val check = option(myVal)(false)(_.isOK)

You'll have to ask people more expert in Scala than I if you want to do this with less currying or by pimping the Option class, but notice how this basically boils down to the pattern matching that Jhonny Everson suggested.

Upvotes: 1

Sue C
Sue C

Reputation: 326

Jhonny Everson is right. Pattern matching is the answer. In this case, the pattern in his answer is equivalent to exists

scala> Some(new MyClass) :: None :: Nil map(_.exists(_.isOK))
res12: List[Boolean] = List(true, false)

Upvotes: 1

Johnny Everson
Johnny Everson

Reputation: 8601

You are applying getOrElse in a Option[MyClass] with Boolean, so, their common superclass is Any.

You should pattern match:

val check = myVal match {
  case Some(c) => c.isOk
  case None => false
} 

Upvotes: 6

senia
senia

Reputation: 38045

You are trying to call method isOk on base class of MyClass and Boolean (Any).

Try this:

scala> class MyClass(b: Boolean) { def isOk = b }
defined class MyClass

scala> val myVal = Some(new MyClass(true))
myVal: Some[MyClass] = Some(MyClass@35d56bbe)

scala> myVal.map{_.isOk}.getOrElse(false)
res0: Boolean = true

scala> myVal.getOrElse(new MyClass(false)).isOk
res1: Boolean = true

Upvotes: 17

vertti
vertti

Reputation: 7879

Because your getOrElse could return false, the common type for MyClass and false is Any.

Upvotes: 1

Jesper
Jesper

Reputation: 206786

You are calling getOrElse on an Option[MyClass]. You're passing a Boolean as a parameter to getOrElse. What happens is that Scala is translating the option to an Option[Any], because Any is the most specific common type of MyClass and Boolean.

Pass a MyClass (or a subclass of MyClass) to getOrElse instead of false.

Upvotes: 4

Tomasz Nurkiewicz
Tomasz Nurkiewicz

Reputation: 340693

Works as designed. This expression:

myVal.getOrElse(false)

returns either unwrapped MyClass instance or (if Option is actually None) - false. The only common type of MyClass and Boolean is... Any. And this is what you are seeing.

In order for this to work you must return something compatible with MyClass from getOrElse():

myVal.getOrElse(new MyClass).isOk

Or maybe you want to implement null-object pattern:

object MyClass {
  val Empty = new MyClass
}

myVal.getOrElse(MyClass.Empty).isOk

Upvotes: 8

Related Questions