-
-
Notifications
You must be signed in to change notification settings - Fork 747
Add std.typecons: Optional, a version of Nullable without alias get this
#7065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add std.typecons: Optional, a version of Nullable without alias get this
#7065
Conversation
|
Thanks for your pull request and interest in making D better, @FeepingCreature! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub fetch digger
dub run digger -- build "master + phobos#7065" |
std/typecons.d
Outdated
| Constructor initializing `this` with `value`. | ||
| Params: | ||
| value = The value to initialize this `Nullable` with. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copypaste error in docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops thanks
You saw nothing. NOTHING.
7cd307f to
a5d7023
Compare
Could you please explain why |
|
|
| formatValue(writer, _value.payload, fmt); | ||
| else | ||
| put(writer, "Optional.absent"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is a non const and const version needed and not just a const version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
toString should still work if Optional embeds a data type with non-const toString (as is, sadly, the default).
| { | ||
| if (!_present) | ||
| { | ||
| throw missingValueException_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be a recoverable error imo. It's more normal (and I think cleaner) for optional types to be more about asking for permission than asking for forgiveness. I'd suggest either an assertion or an Error as opposed to an Exception.
Here's an example showing how attempting to unwrap an empty optional value in Rust produces a panic (unrecoverable error) and accessing an Option's value is normally done via pattern matching (i.e. checking state first, and then accessing value): http://www.ameyalokare.com/rust/2017/10/23/rust-options.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the defining difference between Nullable and Optional. The entire reason why get can be alias this in Nullable is that accessing a Nullable that is null is undefined (check the doc for Nullable.get!), so from a certain perspective Nullable!T to T is an operation that "cannot fail" and is thus "safe". It is thus 100% deliberate for Optional.value to throw an Exception, because the state of "Optional without a value" is a fully defined one and thus one that must result in ordinary control flow in all methods. (That's why valueUnsafe says "unsafe".)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FeepingCreature this is not what normally distinguishes an optional type from a nullable type.
The core difference between option types and nullable types is that option types support nesting (Maybe (Maybe A) ≠ Maybe A), while nullable types do not (String?? = String?).
https://en.wikipedia.org/wiki/Option_type
Very often, the fundamental motivation for using an optional type is to not involve exceptions. Wrongly attempting to access the value of an optional type is a programmer error, in the same sense attempting to index an out-of-bounds array index is a programmer error. (In which case D produces an unrecoverable RangeError, not an exception.)
a5d7023 to
46a5acd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Optional should provide a range interface
- I think this should be added to a new module
- Perhaps implement
opDispatchto allow safe access to methods of the stored value - I would expect to be able to do something like this:
some(3)andnone!int, to indicate that a value is present or not - I'm questioning whether there should be a
get/valuemethod at all. Instead all access to the value should be performed using algorithms
|
I agree with @jacob-carlborg about either making Optional a range or make
it very easy to acquire a range over it, as well as adding Some and None. I
don't have a strong opinion about what module the definition goes in.
I do strongly disagree with the opDispatch suggestion, though. This would
make it difficult to change the API later without breaking dependent code,
and I don't believe this is a normal thing for optional types to do in the
standard libraries of other languages which support them.
|
No, because they have built-in language support. Or the language does not support anything like |
|
@jacob-carlborg I'm not sure what you mean by built-in language support. Of the optional type implementations I've looked at in other languages, you always explicitly |
I mean that in other language like Swift and C# it's possible to do something like this (here with D syntax): Object? a;
string b = a?.toString ?? "foobar";In the above example, if |
In your example |
That's not the recommend way to do it in Scala, for example. It's recommended to use algorithms like Swift has a different approach. It supports optional chaining and optional nil-coalescing [2], which is basically my if let starPath = imagePaths["star"] {
print("The star image is at '\(starPath)'")
} else {
print("Couldn't find the star image")
}[1] https://danielwestheide.com/blog/2012/12/19/the-neophytes-guide-to-scala-part-5-the-option-type.html [2] https://developer.apple.com/documentation/swift/optional |
It's an optional Object in Swift: https://developer.apple.com/documentation/swift/optional. |
|
I agree with @jacob-carlborg on everything as well and also strongly with @pineapplemachine disagreement about opDispatch. It's iffy at best to have an opDispatch inside a wrapper type because as mentioned, it will hinder the api. What if I put a range in the wrapper (that's a range and has an opDispatch) and I call .front? Please no. |
Yes, and having optional be a range will enable the scala approach. We can't have Swift's approach without language support. The best I think I at least came up with is this |
|
Was just reading about this in Swift after you posted some links. I stand corrected. I wasn't thinking about the safe member access or unwrapping member access operators. I for one would be happy to introduce You can at least easily get most of the same As for map and filter and so on, these are not about accessing properties of the enclosed value but are about using a range interface to enumerate the optional's zero-or-one element. You still need to actually unwrap the value at the end of the maps and filters and whatever. This is not a call for opDispatch but an argument for adding a range interface. (Like you mentioned before, and I agree with this.) Potentially of interest is mach's optional type implementation: https://github.com/pineapplemachine/mach.d/blob/master/mach/types/option.d |
|
I'd much rather have #3915 revived and worked on instead of another nullable. Plus all the feature that are requested in this thread (except for jacob's opDispatch) are already in the optional dub package - and is already being used and proven. Which I'm more than happy to put in to phobos if desired. |
|
There should be a method that returns the value if present, otherwise the passed in value. It can be called |
|
It seems I have aesthetic disagreements about "how an Optional type should behave" with pretty much everybody here. Fun! It seems a lot of people are looking at
You are able to do this: Anyway, these all - aside range support, which I dislike for other reasons - seem like wishlist features, not necessary requirements. |
Well, if there are no improvements over |
|
The improvement over |
I don't think adding an exact copy of a whole type to fix a bug in the original type is the way to go. I think we should press harder on PR #7060 and get that in. |
|
Looks like we're going with improving |
|
Deprecation got merged! |
It's basically a copypaste.
Follow-up to #7060 .
Changes:
.getis now.value(.getwas always a weird naming); behavior is well-defined in the absence of a value. Old toString methods have been removed.nullifyhas no equivalent. Everything else is the same, down to the unittests.applyhas been changed to work with both.