June 11, 2019
On Tuesday, 11 June 2019 at 18:01:32 UTC, Nick Treleaven wrote:
> I'd actually rather have language-assisted optional types than features like function overloading (a convenience feature, sometimes abused), because it means *the type system can model runtime checks as a static type guarantee*. They are a game changer.

I don't disagree, but one often end up creating a Nobody subclass singleton that is used as a replacement for null. Although that does have benefits (e.g. if you call a nobody.name() you get "nobody" rather than a crash).
June 11, 2019
On Tuesday, 11 June 2019 at 19:02:12 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 11 June 2019 at 18:01:32 UTC, Nick Treleaven wrote:
>> I'd actually rather have language-assisted optional types than features like function overloading (a convenience feature, sometimes abused), because it means *the type system can model runtime checks as a static type guarantee*. They are a game changer.
>
> I don't disagree, but one often end up creating a Nobody subclass singleton that is used as a replacement for null. Although that does have benefits (e.g. if you call a nobody.name() you get "nobody" rather than a crash).

If there’s a possibility your class can be null then it should probably be an optional. Or lazy. Have not seen this pattern you describe in swift scala or Kotlin.
June 12, 2019
Am 11.06.19 um 12:01 schrieb FeepingCreature:
> Ranges are not monads. We may wish we had a concept of monads
> but we don't; ranges are not a general replacement for any conceivable
> container.

Yes, ranges are not a general replacement for the concept of a monad, but the concept "range" is basically a monad:

Type constructor: For any type T, a range of T is something for that isInputRange!T is true (yes, technically that's not a single "type", but rather something like a typeclass, but it is close enough).

Type converter (unit / return): unit: T -> Range T, x -> only(x)

Combinator (bind / >>=): (Range T, T → Range U) → Range U, (r,f) ->
r.map!f.joiner

Obviously, using these definitions, unit is both a left- and right-identity for bind and bind is essentially associative. So aside from the fact that there is no "real" type constructor because there is no actual general range type, I believe this qualifies the concept of a range as a monad.


All of this aside, I agree that ranges are not a good general replacement for every container. I do however disagree with you in this specific case. I personally have to use Java a lot at work and I am regularly annoyed by the fact, that Optionals in Java are not Iterables (or Streams...). I like the aproach of Scala's and Vavr's "Option" much more, which implement the corresponding Iterable interfaces. It makes a lot of things just that much easier, e.g.

List.of(Optional.of(42), Optional.of(1234)).stream()
    .filter(Optional::isPresent)
    .map(Optional::get)

becomes

List.of(Optional.of(42), Optional.of(1234)).stream()
    .flatMap(o -> o)

or in D:

only(optional(42), optional(1234))
    .filter!(o -> o.present)
    .map!(o -> o.value)

becomes

only(optional(42), optional(1234))
    .joiner


Of course it is still debatable if we want this, but in my opinion this is a great improvement.

If we decide against Optional being a "real" optional type as used in other more functional languages, I'm actually against adding it to the standard library. I'd much prefer to fix the `alias get this` issue of Nullable in that case.

Don't get me wrong however, I'd love a real Optional type. But so far, the `optional` dub package works reasonably well for me. I like the pattern matching functionality in particular, but it also provides other goodies you'd expect from a real optional type, too, e.g. `orElse`. Your suggested implementation is lacking these unfortunately.

June 12, 2019
Am 11.06.19 um 20:01 schrieb Nick Treleaven:
>> Making a class type optional just means that you're adding *another* type that also means "absent" at a different level of abstraction.
> 
> Re-use null to mean absent.

Java has the exact same problem with all non primitive types being nullable. They also solved it on the library level with Optional. In Java, you can never extract null from an Optional:

If you try to constrcut an Optional from null using Optional.of, you will get an exception and if you use Optional.ofNullable, the result will simply be an empty Optional (which technically probably holds the null reference inside, but you can never retrieve it because trying to access it will throw an exception).

This approach seems to have worked out quite well for Java, so I suggest we try a similar approach.

Of course the situation is a bit easier for Java than for D because they only need to care about class types and we have to make everything work for pointers, arrays, associstive arrays, ..., as well, and the desired semantics are not that clear for those... But at least for classes, I think the route that Java took is the correct one.
June 12, 2019
Am 11.06.19 um 13:47 schrieb FeepingCreature:
> My impression is that the distinction turns on whether the null case can be expected to occur in "normal use" of the type. As `Nullable` says, accessing `get` while the `Nullable` `isNull` is *undefined*; that is, at the level of accessing missing array keys. (Except you can access them implicitly.) From this I infer that `Nullable` is mostly supposed to be non-null, maybe to enable delayed initialization or the like, which would justify the `alias get this` and match the unittests.
> 
> This is a *very* different thing from an `Optional` type! An `Optional` type is expected to be unset in normal operation. (That's why my implementation throws an Exception on access, not an Error.) That is, the user is expected to regularly encounter `Optional`s whose value is absent, and handle this state in his ordinary control flow. Hence no implicit alias, and no AssertError on access.

Exactly. This is why I'd expect a "real" Optional type to provide a lot more utilities to actually work with it, such as pattern matching, orElse, iterating, mapping, flatMapping, filtering, etc.
June 12, 2019
On 2019-06-11 15:55, FeepingCreature wrote:

> We have "map for Optional/Nullable", that's `apply`.

And the rest of the algorithms?

-- 
/Jacob Carlborg
June 12, 2019
On Wednesday, 12 June 2019 at 09:53:03 UTC, Jacob Carlborg wrote:
> On 2019-06-11 15:55, FeepingCreature wrote:
>
>> We have "map for Optional/Nullable", that's `apply`.
>
> And the rest of the algorithms?

As the saying goes, "PRs welcome." Personally I get along fine without them, so I didn't see the need to include them. For instance, we have a local orElse implementation hanging around. The point of having `Optional` in the stdlib is that the rest of the ecosystem can standardize around the data structure. There is none such need for algorithms, especially oneliners.
June 12, 2019
On Wednesday, 12 June 2019 at 10:52:22 UTC, FeepingCreature wrote:
> On Wednesday, 12 June 2019 at 09:53:03 UTC, Jacob Carlborg wrote:
>> On 2019-06-11 15:55, FeepingCreature wrote:
>>
>>> We have "map for Optional/Nullable", that's `apply`.
>>
>> And the rest of the algorithms?
>
> As the saying goes, "PRs welcome." Personally I get along fine without them, so I didn't see the need to include them. For instance, we have a local orElse implementation hanging around. The point of having `Optional` in the stdlib is that the rest of the ecosystem can standardize around the data structure. There is none such need for algorithms, especially oneliners.

These algorithms are already there if it's implemented as a range. There's no need to reimplement them.
June 12, 2019
On Wednesday, 12 June 2019 at 11:56:57 UTC, aliak wrote:
> There's no need to reimplement them.

... I would hope.


June 12, 2019
On Wednesday, 12 June 2019 at 11:56:57 UTC, aliak wrote:
> These algorithms are already there if it's implemented as a range. There's no need to reimplement them.

For instance, `orElse` is the very clear and understandable `5.optional.chain(7.only).front`.

Which, if you are at all interested in readability, you'll `alias orElse = (a, b) => a.chain(b.only).front;` anyways. At which point you might as well `alias orElse = (a, b) a.present ? a.value : b;`