Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Option.map (null) returns Some (null)

Tags:

null

scala

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?

like image 733
WelcomeTo Avatar asked Apr 15 '15 20:04

WelcomeTo


3 Answers

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)
like image 82
Ben Reich Avatar answered Nov 10 '22 09:11

Ben Reich


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.

like image 2
Michael Zajac Avatar answered Nov 10 '22 11:11

Michael Zajac


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).

like image 1
Nikita Avatar answered Nov 10 '22 11:11

Nikita