Jonathan Spiller
Jonathan Spiller

Reputation: 1895

Scala Option.fold Behaves differently in and out of function

Take the simple function:

def makeUpper(input: Option[String]): String = 
    input.fold("b"){ _.toUpperCase }

makeUpper(Some("a"))   // A
makeUpper(None)        // b

This is as expected.

Now, the same code, but outside the function:

Some("a").fold("b"){ _.toUpperCase }   // A
None.fold("b"){ _.toUpperCase }        // error: value toUpperCase is not a member of Nothing

Note:

Option.empty[String].fold("b"){ _.toUpperCase }   // b

Questions:

  1. Why the different behavior? Why the error outside the function?

Is the function casting the input None to a "definitive" Option.empty[String]?

  1. What is the correct way to handle returns from function that return Option (Some or None)... Do we always have to process the return value inside another function to avoid the above issue?

What am I missing?

Upvotes: 1

Views: 187

Answers (2)

sarveshseri
sarveshseri

Reputation: 13985

Scala compiler can infer types.

Since you have defined the function parameter input as Option[String], even if you pass None, the compiler knows that it is an Option[String] and appropritately types it.

Which is kind of doing this,

val none: Option[String] = None
none.fold("b"){ _.toUpperCase }

Or this,

(None: Option[String]).fold("b"){ _.toUpperCase }

When directly using None without any extra information, the compiler still tries to infer best guess useing only available information which is None extends Option[Nothing] and hence treats it as Option[Nothing]. Which is equivalent to,

val none: Option[Nothing] = None
none.fold("b"){ _.toUpperCase }

or this,

(None: Option[Nothing]).fold("b"){ _.toUpperCase }

Notice that there is no indication of any relation to String in this case.

As long as you help the compiler by telling your intended type even at one place. It will be able to infer intended types for other relatable places.

scala> val noneIntOpt = noneStringOpt
// val noneIntOpt: Option[String] = None

scala> val noneIntOpt = noneStringOpt.map(_.length)
// val noneIntOpt: Option[Int] = None

Upvotes: 3

Tim
Tim

Reputation: 27356

None is defined as:

case object None extends Option[Nothing]

So when you call a method on the "contents" of None you are calling a method on a value of type Nothing. This type does not have a method toUpperCase so you get an error.

However Nothing is compatible with every type, and Option is covariant with its type parameter, so Option[Nothing] is compatible with Option[String]. This is why None can be passed to your makeUpper method. Once the type of the contents is known to be String the toUpperCase method can be called on it.

Upvotes: 3

Related Questions