Vavr: Option .map should probably gracefully handle Functions that return null.

Created on 11 Jul 2018  Â·  6Comments  Â·  Source: vavr-io/vavr

See the source below.

default <U> Option<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper, "mapper is null");
    return this.isEmpty() ? none() : some(mapper.apply(this.get()));
}

If the mapper can potentially return nulls, this will cause a Some to be created with a null value. This prevents chaining such as Option.of(someObj).map(someFunctional).map(someOtherFunctional).

I know Option is not meant to mimic Java's implementation, but I feel the Optional has this right.

question

Most helpful comment

Some features that are possible because of our Option implementation:

  • Our Map supports null keys and values. I.e. map.get(k) may return Some(null) if the k is associated with null, and None if no value is associated with k.
  • Symmetrie: option.toTry().toOption() = option (works for both, Some and None)

That's how things should work. My experience is that other interpretations are eligible but not canonical.

All 6 comments

Hi @chantrybjss,

thank you, that is one of the most controversial discussed topics regarding Vavr.

Vavr was originally designed to bring some of the Scala goodness to the Java world, including Option’s semantics. Today, after developing Vavr for more than 4 years, I would handle things a little different.

In the first place, a library that extends an existing language, must not duplicate features, especially when using different semantics (like Vavr does with Option).

Second, similar features, like Try, Either, ..., should align to existing native API’s (like Optional’s) in order to not confuse developers.

I already thought about changing Vavr by completely removing Option and align the Try API to the one of Optional. What would we loose?

The main features of Vavr’s Option are

  • it is iterable
  • it is serializable
  • it is convertible within the Vavr space

Convertability currently does not scale very well because of tight coupling. This is a solvable problem.

I give nothing about so-called serializability. Sending objects through a wire can be achieved without extending Serializable.

The most important aspect (of all Vavr _values_) is that they are iterable. It enables us to compose things in a gereric way.

—-

What is your main reason to use Vavr’s Option. Could you also use Optional? What hinders you/what would it take to increase interoperability between Vavr and Java’s Optional?

Thx!

In this case, I have actually reverted to using Optional in my code.

My main concern is that the map method allows Option to create a Some with a null value, which I had assumed would be impossible.

I have looked closer at the source and it appears map is not unique in this respect. I had assumed some() was a private scoped method, but it is actually possible to create Some objects of null type (seemingly by design).

It is my opinion that it should not be possible to create null valued Some objects. However, I do not want to get into a debate about API design, so unless there is some other reason, I think it is safe to close this issue.

@chantrybjss as a workaround you could use this pattern:

option.map(nullReturningFunction).flatMap(Option::of)

The flatMap(Option::of) would make sure you switch over from Some(null) to None.

I think there are subtle philosophical differences between Optional and Option. Optional starts from a universe of null pointers and tries to make it easier for functional programming to operate in that universe. Option starts from a universe of functional constructs and includes a bridge (Option.of) to get data out of that other universe.

Vavr-native code shouldn't use null, and if you're talking to a non-Vavr-native library that's documented to return nulls (such as java.util.collection), Vavr's design encourages you to wrap it in Options; but Option isn't _intended_ as a mechanism to prevent NullPointerExceptions.

The way to think about nullable code, I think, is a bit like unsafe in C# or Rust—don't spend any more time there than you have to, be careful when you do and don’t let the nulls escape into your “safe” core code.

Some day, maybe @Nullable will be standardized and enforcable and magically applied to the whole compilation scope by static analysis, but until then, as they say in Python, “explicit is better than implicit”. If you forget the Option.of call, that's a programming error that should fail fast.

P.S. Some interesting discussion of the history of Optional here, including links to mailing list archives where you can dig into the fights over “why doesn't it behave like [various other Option implementations and Option-like things that all behave differently from one another, including Fugue's which, like Vavr's, was inspired by Scala]”.

If you want to go down that rathole, the Loose Ends: Optional thread is particularly interesting.

But Guava Optional is not trying to be any of the things its detractors
want it to be. (I find Ben Hutchison's arrogant accusation of ignorance
particularly galling.) It is only an alternative -- a better alternative --
to the practice of using null to signify a missing value, especially a
missing return value. Without it, users of an interface with methods that
can return null to signify "no value" can -- and do, repeatedly -- forget
to check for nullity and find out about their mistake only at runtime, if
ever. With it, users are forced to at least acknowledge the need to check
for a missing value. It can streamline the common cases of supplying a
default value or, with Java 8 lambdas, an exception to throw when the value
is missing.

Tim Peierls, 2013-06-04

Some features that are possible because of our Option implementation:

  • Our Map supports null keys and values. I.e. map.get(k) may return Some(null) if the k is associated with null, and None if no value is associated with k.
  • Symmetrie: option.toTry().toOption() = option (works for both, Some and None)

That's how things should work. My experience is that other interpretations are eligible but not canonical.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

liviamoroianu picture liviamoroianu  Â·  3Comments

Pyeroh picture Pyeroh  Â·  3Comments

Vivek-Patil picture Vivek-Patil  Â·  3Comments

manu-m picture manu-m  Â·  6Comments

yarulan picture yarulan  Â·  5Comments