www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Optional and NotNull version 0.5.0 - swift optional like and scala

reply aliak <something something.com> writes:
Hi

See: https://optional.dub.pm

I've totally revamped the Optional type and am now quite happy 
with. It has a range interface and safe dispatching and can be 
used to 1) avoid null dereferencing, 2) have non-null guarantees, 
and 3) show clear intent where there may or may not be a value. 
It's also  nogc and  safe and mutation of the original object 
during safe dispatching works as well.

Some code examples:

===
import optional;
import std.stdio: writeln;

class Residence {
     auto numberOfRooms = 4;
}
class Person {
     Optional!Residence residence;
}

auto john = some(new Person());

john.dispatch.residence.dispatch.numberOfRooms.writeln; // safe, 
prints "[]"
john.dispatch.residence = new Residence();
john.dispatch.residence.dispatch.numberOfRooms.writeln; // prints 
"[4]"

if (auto res = john.dispatch.residence.unwrap) {
     writeln(res.numberOfRooms); // safe, prints "4"
}
===

And since it's a range type as well, you can use it with phobos 
algos (and it provides some primitives found in e.g. scala)

===
import std.algorithm: each;

// Make a function that may or may not parse a string to an int
Optional!int toInt(string str) {
     import std.conv: to;
     scope(failure) return no!int;
     return some(str.to!int);
}

auto x = toInt("1").orElse(0);

toInt("1").each!writeln;

toInt("1").match!(
     (i) => writeln(i),
     () => writeln("nothing there"),
);
===

The readme contains a lot more details.

Some things that are on me list that I need to think about
- Consider a short form for "dispatch". Purely for convenience:
    e.g.: john.d.residence.d.numberOfRooms;
- Consider an auto dispatch (".autoDispatch"?), so that once you 
start a chain you don't need to write "dispatch again:
    e.g.: john.autoDispatch.residence.numberOfRooms;


Some reasonings for design:
- The dispatcher is a completely separate type because Optional 
is a range type and has it's own functions that would be 
impossible to call on a type T without unwrapping first.
- The "safe dispatcher" proxy contains NO functions so that it 
will never trample over a type T.


Cheers,
- Ali
Aug 16
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 Hi

 See: https://optional.dub.pm
Looks great!
 auto john = some(new Person());
Would it also work to leave off the `some` here and skip the first `dispatch` in the following lines? (i.e., make `john` a `Person` rather than an `Optional!Person`)
Aug 16
parent aliak <something something.com> writes:
On Thursday, 16 August 2018 at 15:38:50 UTC, Paul Backus wrote:
 On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 Hi

 See: https://optional.dub.pm
Looks great!
 auto john = some(new Person());
Would it also work to leave off the `some` here and skip the first `dispatch` in the following lines? (i.e., make `john` a `Person` rather than an `Optional!Person`)
Thanks! And aye, it should work fine!
Aug 16
prev sibling next sibling parent reply aliak <something something.com> writes:
On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 It's also  nogc and  safe
No it's not. Not dispatching at least. Dunno why though. Seems safey is because taking an address. Nogc will have to look in to.
Aug 16
parent reply ikod <geller.garry gmail.com> writes:
On Thursday, 16 August 2018 at 16:20:09 UTC, aliak wrote:
 On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 It's also  nogc and  safe
No it's not. Not dispatching at least. Dunno why though. Seems safey is because taking an address. Nogc will have to look in to.
Hello! please, help, how can I use match in nogc code: import optional; void main() nogc { Optional!int i = 1; bool b = true; i.match!( (v) => b, () => false ); } fails to compile: source/app.d(3,6): Error: function `D main` is nogc yet allocates closures with the GC source/app.d(7,9): app.main.__lambda1 closes over variable b at source/app.d(5,10) Thanks!
Aug 20
parent aliak <something something.com> writes:
On Monday, 20 August 2018 at 09:16:18 UTC, ikod wrote:
 On Thursday, 16 August 2018 at 16:20:09 UTC, aliak wrote:
 On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 It's also  nogc and  safe
No it's not. Not dispatching at least. Dunno why though. Seems safey is because taking an address. Nogc will have to look in to.
Hello! please, help, how can I use match in nogc code: import optional; void main() nogc { Optional!int i = 1; bool b = true; i.match!( (v) => b, () => false ); } fails to compile: source/app.d(3,6): Error: function `D main` is nogc yet allocates closures with the GC source/app.d(7,9): app.main.__lambda1 closes over variable b at source/app.d(5,10) Thanks!
Hi, it's because the lambda "(v) => b" allocates. Same would happen with any other function that takes an alias to a lambda and that lambda causes a closure allocation. Changing this to "(int v) => b" seems to make the compiler infer nogc though and it compiles. But it's still a delegate, so I'm not sure why that works and not sure in which situations D infers a nogc delegate. The same thing does not work for algorithm.map for e.g. Cheers - Ali
Aug 20
prev sibling next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 Hi

 See: https://optional.dub.pm

 I've totally revamped the Optional type and am now quite happy 
 with. It has a range interface and safe dispatching and can be 
 used to 1) avoid null dereferencing, 2) have non-null 
 guarantees, and 3) show clear intent where there may or may not 
 be a value. It's also  nogc and  safe and mutation of the 
 original object during safe dispatching works as well.
The readme.md page looks great. You might mention that it works with nogc and safe (I presume Optional and NotNull). One question: Suppose there was a weaksafe that has all the same features safe except that it relaxed the requirement of "No taking the address of a local variable or function parameter." so that you could take the address of a NotNull variable. Are there any reasons why this would be unsafe? I imagine NotNull requires some type of run-time checking, but other than that I don't see why not.
Aug 16
parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Thursday, 16 August 2018 at 18:10:38 UTC, jmh530 wrote:
 On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 Hi

 See: https://optional.dub.pm

 I've totally revamped the Optional type and am now quite happy 
 with. It has a range interface and safe dispatching and can be 
 used to 1) avoid null dereferencing, 2) have non-null 
 guarantees, and 3) show clear intent where there may or may 
 not be a value. It's also  nogc and  safe and mutation of the 
 original object during safe dispatching works as well.
The readme.md page looks great. You might mention that it works with nogc and safe (I presume Optional and NotNull). One question: Suppose there was a weaksafe that has all the same features safe except that it relaxed the requirement of "No taking the address of a local variable or function parameter." so that you could take the address of a NotNull variable. Are there any reasons why this would be unsafe? I imagine NotNull requires some type of run-time checking, but other than that I don't see why not.
Actually " weaksafe" already exists in the form of ` safe` + `-dip1000` - you can take the address of a local variable in ` safe` code and then you get a `scope`-ed pointer, which you're not allowed to escape.
Aug 16
parent aliak <something something.com> writes:
On Friday, 17 August 2018 at 06:59:48 UTC, Petar Kirov 
[ZombineDev] wrote:
 On Thursday, 16 August 2018 at 18:10:38 UTC, jmh530 wrote:
 On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 Hi

 See: https://optional.dub.pm

 I've totally revamped the Optional type and am now quite 
 happy with. It has a range interface and safe dispatching and 
 can be used to 1) avoid null dereferencing, 2) have non-null 
 guarantees, and 3) show clear intent where there may or may 
 not be a value. It's also  nogc and  safe and mutation of the 
 original object during safe dispatching works as well.
The readme.md page looks great. You might mention that it works with nogc and safe (I presume Optional and NotNull). One question: Suppose there was a weaksafe that has all the same features safe except that it relaxed the requirement of "No taking the address of a local variable or function parameter." so that you could take the address of a NotNull variable. Are there any reasons why this would be unsafe? I imagine NotNull requires some type of run-time checking, but other than that I don't see why not.
Actually " weaksafe" already exists in the form of ` safe` + `-dip1000` - you can take the address of a local variable in ` safe` code and then you get a `scope`-ed pointer, which you're not allowed to escape.
Right! There's that. That would most certainly help here. So the first non-safety part of Optional comes from the dispatch function. To support mutations, the pattern is basically: struct Optional(T) { T value; auto dispatch() inout { return inout Dispatcher(&this); } } Now the thing is, the Dispatcher type is actually disabled on copying, construction and identity assignment. So I wonder if this can be marked trusted in theory. Since the address never escapes from a Dispatcher object. And the other part is when two addresses are compared (which is local only): https://github.com/aliak00/optional/blob/83edfef7130ec02992ec2986611718f80167234d/source/optional/dispatcher.d#L42
Aug 17
prev sibling next sibling parent reply Seb <seb wilzba.ch> writes:
On Thursday, 16 August 2018 at 12:25:14 UTC, aliak wrote:
 Hi

 See: https://optional.dub.pm

 [...]
That looks pretty cool! I added optional to run.dlang.io (e.g. https://run.dlang.io/is/912kVG) and the project tester (https://github.com/dlang/ci/pull/288).
Aug 20
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 20 August 2018 at 19:06:36 UTC, Seb wrote:
 [snip]
 That looks pretty cool!
 I added optional to run.dlang.io (e.g. 
 https://run.dlang.io/is/912kVG) and the project tester 
 (https://github.com/dlang/ci/pull/288).
It's interesting that both sumtype and optional have match templates. Maybe scope to combine these projects?
Aug 20
parent reply aliak <something something.com> writes:
On Monday, 20 August 2018 at 19:52:53 UTC, jmh530 wrote:
 On Monday, 20 August 2018 at 19:06:36 UTC, Seb wrote:
 [snip]
 That looks pretty cool!
 I added optional to run.dlang.io (e.g. 
 https://run.dlang.io/is/912kVG) and the project tester 
 (https://github.com/dlang/ci/pull/288).
It's interesting that both sumtype and optional have match templates. Maybe scope to combine these projects?
That'd be cool. Optional uses .match on a "some" or "none" range, while SumType uses it on a union. So ideas on how to go about it?
Aug 22
parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 22 August 2018 at 22:11:05 UTC, aliak wrote:
 On Monday, 20 August 2018 at 19:52:53 UTC, jmh530 wrote:
 It's interesting that both sumtype and optional have match 
 templates. Maybe scope to combine these projects?
That'd be cool. Optional uses .match on a "some" or "none" range, while SumType uses it on a union. So ideas on how to go about it?
In theory, Optional(T) could be implemented as a wrapper around SumType!(T, None), which would let it reuse SumType's match method. I'm not sure if it'd be worth the effort to convert at this point, though.
Aug 22
parent reply aliak <something something.com> writes:
On Wednesday, 22 August 2018 at 22:49:52 UTC, Paul Backus wrote:
 On Wednesday, 22 August 2018 at 22:11:05 UTC, aliak wrote:
 On Monday, 20 August 2018 at 19:52:53 UTC, jmh530 wrote:
 It's interesting that both sumtype and optional have match 
 templates. Maybe scope to combine these projects?
That'd be cool. Optional uses .match on a "some" or "none" range, while SumType uses it on a union. So ideas on how to go about it?
In theory, Optional(T) could be implemented as a wrapper around SumType!(T, None), which would let it reuse SumType's match method. I'm not sure if it'd be worth the effort to convert at this point, though.
THis is true. And might be interesting to try out actually. Can you access the types in a SumType via index? I'm thinking because Optional behaves like a range, and I guess I'd define a SumType!(T, None), then a rough outline may be: struct None {} immutable none = None(); struct(T) { SumType(T, None) opt; T front() { return opt[0]; // what to do here? } } Or I guess I should maybe do it like this?: return opt.match!( (T val) => val, (None) => T.init, );
Aug 24
parent Paul Backus <snarwin gmail.com> writes:
On Friday, 24 August 2018 at 20:59:34 UTC, aliak wrote:
 THis is true. And might be interesting to try out actually. Can 
 you access the types in a SumType via index?

 I'm thinking because Optional behaves like a range, and I guess 
 I'd define a SumType!(T, None), then a rough outline may be:

 struct None {}
 immutable none = None();

 struct(T) {
   SumType(T, None) opt;
   T front() {
     return opt[0]; // what to do here?
   }
 }

 Or I guess I should maybe do it like this?:

 return opt.match!(
   (T val) => val,
   (None) => T.init,
 );
I think this is probably the best way: property T front() { return opt.match!( (T val) => val, (None) { assert(false, "called .front on an empty range"); return T.init; // for return type inference } ); } You could also do it with `tryMatch` and `std.exception.assertNotThrown`. It makes the code a little nicer, but involving exceptions at all seemed like unnecessary overhead to me.
Aug 24
prev sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
 - Consider a short form for "dispatch". Purely for convenience:
    e.g.: john.d.residence.d.numberOfRooms;
Why not .get, like Nullable? As long as you never alias it to this... ;)
Aug 26
parent aliak <something something.com> writes:
On Monday, 27 August 2018 at 05:22:30 UTC, FeepingCreature wrote:
 - Consider a short form for "dispatch". Purely for convenience:
    e.g.: john.d.residence.d.numberOfRooms;
Why not .get, like Nullable? As long as you never alias it to this... ;)
Mmm... get is indicative of getting the value. But we're not getting a value here, we're dispatching/propagating to the underlying type. I just wish we could figure out some way to use operators to some degree in D :(
Aug 27