Should .Map return None if mapping returns null?
JudahGabriel opened this issue · comments
I was surprised by this behavior:
var obj = new { ThisIsNull = (string)null };
var result = Option.Some(obj)
.Map(o => o.ThisIsNull)
.ValueOr("hello");
Expected: "hello"
Actual: null
Isn't this unintuitive behavior? Looking at Java 8's Optional and Rust's std::Option, the result of .Map is None if the mapping produces null.
I realize we can get this functionality with .Map(...).NotNull(), however, the existing behavior remains unintuitive. Thoughts?
Hi,
This is actually by design. You can find some more details here: #14
The argument basically boils down to optionals behaving consistently (e.g. under refactorings), and the unfavorable "value/complexity ratio" of introducing a special case for nulls.
It is true that Java disallows null inside an Optional. Rust, however, doesn't really have the concept of null, so it cannot be directly compared. A more direct comparison would be with Scala and F#, which both allows nulls inside an Option.
/Nils
Your documentation and your reasoning are quite clear-headed! I figured it was a deliberate design choice.
Let me see if I understand your reasoning for this decision:
- Filtering nulls in the library will introduce complexity into the library itself.
- Consistency; inverted operations would no longer return the same result. val.Some().Map(mapping) != mapping(val).Some().
- Doesn't solve all cases of null: users can still make null happen given .Chaining.Null.Value.
If I understand you right, I'm still in favor of filtering nulls. To respond to the above:
- True! But we users don't care about your complexity. We care about making our code simpler. :-) The library filtering out nulls makes our code simpler.
- The consistency argument doesn't matter to me. I've never thought, "Gee, I wonder if changing the order of operation here will affect the output" - rather, my intuitive expectation is that yes, the output may change if the order changes. This is a non-issue to me.
- Just because we can't fix the world doesn't mean we can't make it a little better. :-) As a user, I would not expect .Chaining.Null.Values to work. But I would expect .Map(o => o.NullVal) to be None.
Overriding these is the surprising behavior of allowing nulls into an Option, and "lying" that .HasValue is true. That's unintuitive as a user, particularly for a library intended to deal with the problem of null values.
That's my feedback. This is a great library, and I'll still use it regardless. 👍
Closing this one for now. Feel free to re-open or open a new issue, if something pops up.