7

I want to this code snippet return me None instead of Some(null):

Option(x).map(x.getNullValue) // returns Some(null)

I heard Scalaz library has features to handle such case. So how I can achieve my goal using both: scalaz and standard Scala library?

pnuts
  • 58,317
  • 11
  • 87
  • 139
WelcomeTo
  • 19,843
  • 53
  • 170
  • 286

3 Answers3

10

You could use flatMap together with the Option.apply method here instead of pulling in scalaz:

Option(initialValue).flatMap(x => Option(x.getNullValue))

This works since the Option.apply method treads null intelligently:

val x: String = null
Option(x) //None
Option("foo") //Some("foo")

So if you know the value outright, you can simply do:

Option(x.getNullValue)

You can also use other methods on Option like filter, orElse, or getOrElse, depending on the situation:

Option(initialValue).map(_.getNullValue).filter(_ != null)
Option(initialValue).orElse(Option(x.getNullValue))
Option(x.getNullValue).getOrElse(defaultValue)
Ben Reich
  • 16,222
  • 2
  • 38
  • 59
2

I don't know about scalaz, but in the standard library your only choice really is to filter out the null value. map simply maps A => B and expects that B will not be null.

Example:

object HasNull {
    def getNull: Any = null
}

scala> Option(HasNull).map(_.getNull).filter(_ != null)
res24: Option[Any] = None

Or

scala> Option(HasNull).flatMap(a => Option(a.getNull))
res25: Option[Any] = None

Alternatively, you can use a little implicit magic to avoid the Option boilerplate:

implicit def toOpt[A](a: A): Option[A] = Option(a)

scala> Option(HasNull).flatMap(_.getNull)
res3: Option[Any] = None

Using flatMap still is the key, because it expects an Option[B]. But getNull is of type B, so the implicit conversion will be used, which will wrap the nullable in Option.apply again.

Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
1

As others already wrote, you can do it using flatMap. And a very similar approach is:

case class User(name: String)
val users = List(null, User("John"), User(null))
for{
  userDb <- users
  user <- Option(userDb)
  name <- Option(user.name)
} yield name

The problem with None is that you do not know which None you get: user doesn't exist or name? In this case scalaz can help you:

for{
  userDb <- users
  user <- Option(userDb) \/> "No user found."
  name <- Option(user.name) \/> "No name provided."
} yield name

But this is another story. You can find awesome explanation about this use case here (video).

Nikita
  • 4,435
  • 3
  • 24
  • 44