Reputation: 230028
Given an Option, what is the idiomatic way to get its value or throw an exception trying?
def foo() : String = {
val x : Option[String] = ...
x.getOrException()
}
Upvotes: 84
Views: 93037
Reputation: 295
Since other answers suggest using tricky syntax constructs or using functional error handling, an extension method seems like a better fit:
object OptionExtensions {
extension [T] (opt: Option[T]) {
def getOrThrow(exception: () => Exception): T = {
opt match
case Some(value) =>
value
case None =>
throw exception()
}
}
}
x.getOrThrow(() => Exception("Failed to get X"))
Upvotes: 3
Reputation: 387
I hope this will help you to understand how to represent errors (and generally effects) using types.
Use Option
to return optional values. For example - fail to find entity in storage.
Use Option(possiblyNull)
to avoid instances of Some(null)
.
Use Either[Error, T]
to report expected failure. For example - email format is wrong, cannot parse a string to a number, etc.
Model your errors as ADTs (simply speaking kind of type hierarchies) to use it, for example, on the Left of the Either to represent more complex error scenarios.
Throw Exception
only to signal unexpected and not-recoverable failures. Like missing config file.
Use Either.catchOnly
or Try
or Cats.IO
(advanced) rather than a catch block for handling unexpected failures. Hint: You can still use ADT but extend them from throwables. More about Either
vs Try
.
Use Validated
data-type from Cats lib to accumulate errors rather than fail-fast (Either
), but prefer Either's on module-level to simplify the composition of the program (to have the same types). For example - form data validation, parsing errors accumulation.
Use mentioned types and don't optimize program preemptively - since most probably, bottle-necks would be in business logic, not in effect types.
Such an approach will simplify maintenance and updates of your code since you can reason about it without going to implementation specifics (aka local-reasoning). Also - reduce bugs - you cannot miss an error in the type. And compose the program easier (with help of map
, flatMap
and other combinators) - since it's simpler on type level, rather than with non-local exceptions and side-effects.
More about learning functional Scala.
But be aware that sometimes with this approach types could stack up and it could become harder to compose things. Given, for example: x: Future[Either[Error, Option[T]]]
What you can do:
map
and flatMap
in combination with pattern-matching to compose different values of such types, for example:x.faltMap { case Right(Some(v)) => anotherFuture(v); case Left(er) => ... }
Future[Option[T]]
And finally, in your case one option will be:
def foo() : Either[Error, String] = {
val x : Option[String] = ...
x match {
case Some(v) => Right(v)
case None => Left(Error(reason))
}
}
Upvotes: 15
Reputation: 4453
Scala now support this operation on maps using getOrElse()
method, see documentation here
As pointed out already, throwing an exception in Scala is an expression as well.
So you can do the following:
myMap.getOrElse(myKey, throw new MyCustomException("Custom Message HERE")
Upvotes: 2
Reputation: 1991
(EDIT: this is not the best or most idiomatic way to do it. I wrote it when I was not familiar with Scala. I leave it here for an example of how not to do it. Nowadays I would do as @TravisBrown)
I think it really boils down to two things:
If at that point in your code you expect the value to be there, and in the remote case that it isn't you want your program to fail fast, then I would only do a normal get
and let Scala throw a NoSuchElementException
if there was no value:
def foo() : String = { val x : Option[String] = ... x.get }
If you want to handle the case differently (throw your own exception) I think a more elegant way would look like this:
def foo(): String = { val x: Option[String] = None x match { case Some(value) => value case None => throw new MyRuntimeException("blah") } }
And of course if you want to supply your own alternative value for the case that the Option
is None
you would just use getOrElse
:
def foo(): String = { val x: Option[String] = None x.getOrElse("my alternative value") }
Upvotes: 52
Reputation: 8495
Just use the .get method.
def get[T](o:Option[T]) = o.get
It will throw a NoSuchElementException if o is an instance of None.
Basically, I would work with options like this:
def addPrint(oi:Option[Int]) = oi.map(_+1).foreach(println)
addPrint(Some(41))
addPrint(Some(1336))
addPrint(None)
to avoid your specific question.
Upvotes: 14
Reputation: 139038
A throw
"statement" is really an expression in Scala, and it has type Nothing
, which is a subtype of every other type. This means you can just use plain old getOrElse
:
def myGet[A](oa: Option[A]) = oa.getOrElse(throw new RuntimeException("Can't."))
You really, really shouldn't be doing this, though.
Upvotes: 143