www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - An optional/maybe type with range semantics

reply aliak <something something.com> writes:
Alo,

Just finished up a first take on an optional type for D. It's 
essentially a mix of Nullable and std.range.only, but with a lot 
more bells and whistles. I would love to hear any feedback on 
code, or features, or bad design or potential for better designs 
from anyone who's interested :)

Code: https://github.com/aliak00/optional
Dub: https://code.dlang.org/packages/optional
Docs: https://aliak00.github.io/optional/

Things to do:
* Handle dispatching of static fields/method (see code below)
* Implement flatmap/decide if flatmap should be in here
* Figure out what kind of range this should be (currently its 
just an input range - should it be more?)
* Implement some phobos algorithms as returning optionals (e.g. 
countUntil)

Anyway, here's some code to peak at a few of the supported 
features:

auto a = some(3);
auto b = a + 4; // opUnary/Binary/Equals (so far) supported

a = none;
a++ // noop

auto p = no!(int*);
assert(a == *p) // none == none
p = null;
assert(a == *p) // null handled

struct A {
   struct B { void g() {} }
   B f() { return B(); }
}

auto  b = no!(A*);
b.dispatch.f.g // dispatch is safe, noop

b = new A;
b.dispatch.f.g // makes calls now because non empty.

Cheers,
- Ali
Feb 25
next sibling parent reply Dukc <ajieskola gmail.com> writes:
On Sunday, 25 February 2018 at 18:03:35 UTC, aliak wrote:
 Alo,

 Just finished up a first take on an optional type for D. It's 
 essentially a mix of Nullable and std.range.only, but with a 
 lot more bells and whistles. I would love to hear any feedback 
 on code, or features, or bad design or potential for better 
 designs from anyone who's interested :)

 Code: https://github.com/aliak00/optional
 Dub: https://code.dlang.org/packages/optional
 Docs: https://aliak00.github.io/optional/

 Things to do:
 * Handle dispatching of static fields/method (see code below)
 * Implement flatmap/decide if flatmap should be in here
 * Figure out what kind of range this should be (currently its 
 just an input range - should it be more?)
 * Implement some phobos algorithms as returning optionals (e.g. 
 countUntil)

 Anyway, here's some code to peak at a few of the supported 
 features:

 auto a = some(3);
 auto b = a + 4; // opUnary/Binary/Equals (so far) supported

 a = none;
 a++ // noop

 auto p = no!(int*);
 assert(a == *p) // none == none
 p = null;
 assert(a == *p) // null handled

 struct A {
   struct B { void g() {} }
   B f() { return B(); }
 }

 auto  b = no!(A*);
 b.dispatch.f.g // dispatch is safe, noop

 b = new A;
 b.dispatch.f.g // makes calls now because non empty.

 Cheers,
 - Ali
Honestly, I fail to see the idea behind this... Ranges do not need any nullability on top of them IMO, because an empty range can already be used to denote a kind of "default", "unassigned" or "nothing" - type of value. On the other hand I may just be missing something... do you have an example use case for this where phobos ranges would be a bad option? Anyway, good effort.
Feb 26
parent reply Meta <jared771 gmail.com> writes:
On Monday, 26 February 2018 at 15:21:27 UTC, Dukc wrote:
 Honestly, I fail to see the idea behind this... Ranges do not 
 need any nullability on top of them IMO, because an empty range 
 can already be used to denote a kind of "default", "unassigned" 
 or "nothing" - type of value.

 On the other hand I may just be missing something... do you 
 have an example use case for this where phobos ranges would be 
 a bad option?

 Anyway, good effort.
The idea is to treat `Option!T` as a regular input range so it can be used with all the regular range algorithms without special casing it. You're right in that the null/non-null dichotomy is equivalent to the notion of a range being empty or non-empty.
Feb 26
parent reply Dukc <ajieskola gmail.com> writes:
On Monday, 26 February 2018 at 15:27:11 UTC, Meta wrote:
 The idea is to treat `Option!T` as a regular input range so it 
 can be used with all the regular range algorithms without 
 special casing it. You're right in that the null/non-null 
 dichotomy is equivalent to the notion of a range being empty or 
 non-empty.
I kinda start to see the idea... Granted, nullable is in a way a range that can hold exactly one or exactly zero elements. Not a bad idea at all. But shouldn't it store the value internally as a pointer, not as an array, to save a bit space? When empty, it would point to null.
Feb 26
parent reply aliak <something something.com> writes:
On Monday, 26 February 2018 at 16:02:58 UTC, Dukc wrote:
 I kinda start to see the idea... Granted, nullable is in a way 
 a range that can hold exactly one or exactly zero elements. Not 
 a bad idea at all.
Aye, ranges do not need nullability indeed. Optional doesn't need to adhere to the range spec of course, but it does provide some conveniences. There's a d-forum thread here on it actually: https://forum.dlang.org/thread/l87ivq$263r$1 digitalmars.com There's a link in there on scala's optional type which is a fun read. Here's a bit on the current "brokenness" nature of java's optional: https://developer.atlassian.com/blog/2015/08/optional-broken/ I like how it specifies intent (on the c++ optional): https://www.fluentcpp.com/2016/11/24/clearer-interfaces-with-optionalt/ Boost has a fun write-up on the motivation and design behind its optional type (which was incorporated in to the c++ standard): http://www.boost.org/doc/libs/1_66_0/libs/optional/doc/html/index.html Meta: Is this your stuff btw? -> https://github.com/skirino/d-option :) me thinks I may have gotten some inspiration from you if so, so thanks!
 But shouldn't it store the value internally as a pointer, not 
 as an array, to save a bit space? When empty, it would point to 
 null.
Guess I could do a pointer and call new when i need to store a value instead. Or maybe it's better to do it like above and store as value type with default value and a boolean at the site. Not sure.
Feb 26
next sibling parent Meta <jared771 gmail.com> writes:
On Monday, 26 February 2018 at 20:04:14 UTC, aliak wrote:
 Meta: Is this your stuff btw? -> 
 https://github.com/skirino/d-option :) me thinks I may have 
 gotten some inspiration from you if so, so thanks!
Nope. I'm MetaLang on Github.
Feb 26
prev sibling parent reply Dukc <ajieskola gmail.com> writes:
On Monday, 26 February 2018 at 20:04:14 UTC, aliak wrote:
 Guess I could do a pointer and call new when i need to store a 
 value instead. Or maybe it's better to do it like above and 
 store as value type with default value and a boolean at the 
 site. Not sure.
You do not need a separate boolean field. If someone assigns "none" or calls popFront() just assign the pointer to null. When someone queries empty, you check whether the pointer points to null. The default value is easy too: typeof(element).init. That's the superior design, because it does not need any excess memory for the empty flag.
Feb 27
parent aliak <something something.com> writes:
On Tuesday, 27 February 2018 at 11:58:34 UTC, Dukc wrote:
 On Monday, 26 February 2018 at 20:04:14 UTC, aliak wrote:
 Guess I could do a pointer and call new when i need to store a 
 value instead. Or maybe it's better to do it like above and 
 store as value type with default value and a boolean at the 
 site. Not sure.
You do not need a separate boolean field. If someone assigns "none" or calls popFront() just assign the pointer to null. When someone queries empty, you check whether the pointer points to null. The default value is easy too: typeof(element).init.
Either you misunderstood or my writing was not clear. I meant you'd need a boolean if you were using a value type (was just speaking out loud as another alternative to a pointer or array)
 That's the superior design, because it does not need any excess 
 memory for the empty flag.
It means using new, so not sure how superior it is compare to storing a value type.
Feb 27
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/25/18 8:03 PM, aliak wrote:
 Alo,
 
 Just finished up a first take on an optional type for D. It's 
 essentially a mix of Nullable and std.range.only, but with a lot more 
 bells and whistles. I would love to hear any feedback on code, or 
 features, or bad design or potential for better designs from anyone 
 who's interested :)
 
 Code: https://github.com/aliak00/optional
 Dub: https://code.dlang.org/packages/optional
 Docs: https://aliak00.github.io/optional/
Did you take a look at https://dlang.org/library/std/range/only.html? -- Andrei
Feb 28
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/28/18 12:54 PM, Andrei Alexandrescu wrote:
 On 2/25/18 8:03 PM, aliak wrote:
 Alo,

 Just finished up a first take on an optional type for D. It's 
 essentially a mix of Nullable and std.range.only, but with a lot more 
 bells and whistles. I would love to hear any feedback on code, or 
 features, or bad design or potential for better designs from anyone 
 who's interested :)

 Code: https://github.com/aliak00/optional
 Dub: https://code.dlang.org/packages/optional
 Docs: https://aliak00.github.io/optional/
Did you take a look at https://dlang.org/library/std/range/only.html? -- Andrei
Ah, sorry I missed that you mentioned it. -- Andrei
Feb 28
parent aliak <something something.com> writes:
On Wednesday, 28 February 2018 at 10:55:38 UTC, Andrei 
Alexandrescu wrote:
 On 2/28/18 12:54 PM, Andrei Alexandrescu wrote:
 On 2/25/18 8:03 PM, aliak wrote:
 
 Did you take a look at 
 https://dlang.org/library/std/range/only.html? -- Andrei
Ah, sorry I missed that you mentioned it. -- Andrei
Yeah about that... Maybe I'm looking at this in the wrong way, but I'm not sure I understand how using only would actually work now that I think about it. I can haz enlightenment? :o If I wanted to write (contrived example) a function that may or may not produce a result, with an argument that it will use if present but can do work without (think servers, async, incomplete/partial json data, etc). // a - there's no Only!T, so I don't have a type to work with // b - I need to constrain function to element type and input range // c - and what do i do if range has more than one value in it? auto maybeResult(Range)(Range r) if (isInputRange!Range && is(ElementType!Range == int)) { enforce(r.walkLength <= 1); // should I runtime kill? return v.map!(a => a + 1); // do I return a map of all elements? return v.take(1).map!(a => a + 1); // do I take(1) since that's what this function needs? // what do I do with the rest then if I don't have the enforce above? } And then calling it: auto a = [1]; a = a.maybeResult; // nope, because you can't know what ranges are being used inside. // So you need to create a new var auto b = a.maybeResult; ======================= With an optional this'd be: Optional!int maybeResult(Optional!int a) { return a + 1; } auto a = some(1); a = a.maybeResult; Cheers, - Ali
Mar 01