Reputation: 4992
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
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
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
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
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
Reputation: 7879
Because your getOrElse could return false, the common type for MyClass and false is Any.
Upvotes: 1
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
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