www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.sumtype?

reply Oleg B <code.viator gmail.com> writes:
Hello everyone.

Went to see what is being prepared in the new compiler and saw 
this
https://dlang.org/changelog/pending.html#std-sumtype

Is this realy final variant for including to phobos, not 
std.experimental? No way for getting tag explicitly (it's 
private), no way for getting type by tag, sumtype depend on order 
of types... I don't see any discussion about including it to 
phobos... Why? Nobody cares?

I think algebraic types must be in phobos, but it must be fully 
complited. Otherwise it not neccessary: side libs will fix some 
misses and we don't get unified algebraics.

For example std.json have no [de]serialization and vibe.data.json 
is more useful and used in many projects.

More general question: why phobos so bad structured? No way to 
determine what can be used without gc, that need it (other D 
features like betterC in the same situation). Some basic actions 
requires couple of std libs (std.file, std.path etc). Why 
something things placed in experimental will be in experimental 
during several years, other placed to std directly (sumtype)?

May be it's time to restructure std, and determine algorithm for 
place new code to it (like DIPs)?
Mar 18
next sibling parent reply rm <rymrg memail.com> writes:
 Is this realy final variant for including to phobos, not 
 std.experimental? No way for getting tag explicitly (it's private), no 
 way for getting type by tag, sumtype depend on order of types... I don't 
 see any discussion about including it to phobos... Why? Nobody cares?
I agree. Being able to extract items by type is missing. If the incorrect type is used, it can just assert(0); This makes it really complicated to use in nogc, since you cannot even create delegates to change things in your scope.
Mar 18
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 15:04:13 UTC, rm wrote:
 This makes it really complicated to use in  nogc, since you 
 cannot even create delegates to change things in your scope.
Yes you can. There's even a unit test for this specific use-case: https://github.com/dlang/phobos/blob/8281af7310aa876f2bdeb30fd176d952141b4c96/std/sumtype.d#L2403-L2410
Mar 18
parent reply rm <rymrg memail.com> writes:
On 18/03/2021 17:12, Paul Backus wrote:
 On Thursday, 18 March 2021 at 15:04:13 UTC, rm wrote:
 This makes it really complicated to use in  nogc, since you cannot 
 even create delegates to change things in your scope.
Yes you can. There's even a unit test for this specific use-case: https://github.com/dlang/phobos/blob/8281af7310aa876f2bdeb30fd176d952141b4c96/std/s mtype.d#L2403-L2410
I stand corrected. Now I'm going to have to go back to figure what was wrong with my code back then. I would still like to have tryMatch in nogc.
Mar 18
parent Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 15:37:52 UTC, rm wrote:
 I would still like to have tryMatch in  nogc.
If -preview=dip1008 is ever implemented properly you will get this for free.
Mar 18
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 14:42:03 UTC, Oleg B wrote:
 Hello everyone.

 Went to see what is being prepared in the new compiler and saw 
 this
 https://dlang.org/changelog/pending.html#std-sumtype

 Is this realy final variant for including to phobos, not 
 std.experimental? No way for getting tag explicitly (it's 
 private), no way for getting type by tag, sumtype depend on 
 order of types... I don't see any discussion about including it 
 to phobos... Why? Nobody cares?
There was discussion on Github [1] and on the forums [2]. [1] https://github.com/dlang/phobos/pull/7702 [2] https://forum.dlang.org/thread/dkizmcporviutijhqlcs forum.dlang.org
 May be it's time to restructure std, and determine algorithm 
 for place new code to it (like DIPs)?
I agree that the process for adding new modules to Phobos could do with an overhaul, and that the community should be involved in some way, as it is for DIPs.
Mar 18
next sibling parent reply Oleg B <code.viator gmail.com> writes:
On Thursday, 18 March 2021 at 15:08:19 UTC, Paul Backus wrote:
 [1] https://github.com/dlang/phobos/pull/7702
 [2] 
 https://forum.dlang.org/thread/dkizmcporviutijhqlcs forum.dlang.org
Thank for links, I don't saw these news in November. Since sumtype will be in phobos, can you explane algorithm of [de]serialization of general Sumtype value? In other libs it will be simple for serialization: 1. get tag -> write tag 2. switch for all tags (final switch + static foreach by enum members) -> write value example [1] https://github.com/deviator/sbin/blob/master/source/sbin/serialize.d#L76 and deserialization: 1. read tag 2. final switch + static foreach check tag -> read typed value -> write it to algebraic variable example [2] https://github.com/deviator/sbin/blob/master/source/sbin/deserialize.d#L189 for sumtype I see what I need make own enum (or use number) what I get through match: auto tag = val.match!( (Type1 t1) => 0, // or MyOwnValTag.type1 (Type2 t2) => 1, // or MyOwnValTag.type2 ... etc ); next write tag, in next match write value how to generate this match without mixins? how to get (in general way) type from sumtype val by own tag or number for deserialization? I think these problems have a solutions, but it more complicated (and need write more code) than use enum tags directly.
Mar 18
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 15:58:55 UTC, Oleg B wrote:
 auto tag = val.match!(
   (Type1 t1) => 0, // or MyOwnValTag.type1
   (Type2 t2) => 1, // or MyOwnValTag.type2
   ... etc
 );

 next write tag, in next match write value

 how to generate this match without mixins? how to get (in 
 general way) type from sumtype val by own tag or number for 
 deserialization?
Something like this: alias QualifiedTypes = CopyTypeQualifiers!(typeof(val), val.Types); auto tag = val.match!(v => staticIndexOf!(typeof(v), QualifiedTypes));
Mar 18
next sibling parent reply Oleg B <code.viator gmail.com> writes:
On Thursday, 18 March 2021 at 16:11:06 UTC, Paul Backus wrote:
 Something like this:

 alias QualifiedTypes = CopyTypeQualifiers!(typeof(val), 
 val.Types);
 auto tag = val.match!(v => staticIndexOf!(typeof(v), 
 QualifiedTypes));
why it is not in library? or why tag is private?
Mar 18
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 16:34:15 UTC, Oleg B wrote:
 On Thursday, 18 March 2021 at 16:11:06 UTC, Paul Backus wrote:
 Something like this:

 alias QualifiedTypes = CopyTypeQualifiers!(typeof(val), 
 val.Types);
 auto tag = val.match!(v => staticIndexOf!(typeof(v), 
 QualifiedTypes));
why it is not in library? or why tag is private?
Because I personally haven't needed it for anything, and nobody else has presented me with a convincing use-case for it until now. In general, it is much, much easier to add new features than to remove existing ones, so I try to put off adding a feature until I am absolutely sure it is the right decision.
Mar 18
parent reply Oleg B <code.viator gmail.com> writes:
On Thursday, 18 March 2021 at 16:48:01 UTC, Paul Backus wrote:
 Because I personally haven't needed it for anything, and nobody 
 else has presented me with a convincing use-case for it until 
 now.

 In general, it is much, much easier to add new features than to 
 remove existing ones, so I try to put off adding a feature 
 until I am absolutely sure it is the right decision.
Adding read-only tag is simple and useful change. If I understand code correctly tag is direct index of val.Types, it's right? May be depending on order of types must be fixed too ``` alias S1 = Sumtype!(int, float); alias S2 = Sumtype!(float, int); static assert (is(S1 == S2)); // false by now ``` because no different between S1 and S2 in practice use
Mar 18
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 3/18/21 1:24 PM, Oleg B wrote:
 May be depending on order of types must be fixed too
 
 ```
 alias S1 = Sumtype!(int, float);
 alias S2 = Sumtype!(float, int);
 
 static assert (is(S1 == S2)); // false by now
 ```
 because no different between S1 and S2 in practice use
That requires sorting the types somehow, which isn't really easy or cheap at compile-time. It's the same for std.variant.Algebraic (although it's a runtime error instead of a compile-time one). It's kind of a tradeoff that you have to live with -- nice type checking, but you have to keep the same order for sanity. -Steve
Mar 18
parent Oleg B <code.viator gmail.com> writes:
On Thursday, 18 March 2021 at 17:39:04 UTC, Steven Schveighoffer 
wrote:
 That requires sorting the types somehow, which isn't really 
 easy or cheap at compile-time.

 It's the same for std.variant.Algebraic (although it's a 
 runtime error instead of a compile-time one).

 It's kind of a tradeoff that you have to live with -- nice type 
 checking, but you have to keep the same order for sanity.

 -Steve
As I know it solved in mir.algebraic [1] and code for solving isn't complicated [2] but it will be good if it be in library. [1] http://mir-core.libmir.org/mir_algebraic.html [2] example for some kind of tuple ``` struct Bar(T...) { T val; } template Foo(T...) { enum CmpTypes(T1, T2) = fullyQualifiedName!T1 < fullyQualifiedName!T2; alias Foo = Bar!(staticSort!(CmpTypes, T)); } ```
Mar 18
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 17:24:13 UTC, Oleg B wrote:
 May be depending on order of types must be fixed too

 ```
 alias S1 = Sumtype!(int, float);
 alias S2 = Sumtype!(float, int);

 static assert (is(S1 == S2)); // false by now
 ```
 because no different between S1 and S2 in practice use
union U1 { int n; float f; } union U2 { float f; int n; } static assert(is(U1 == U2)); // fails D is a nominally-typed language, not a structurally-typed one. That means types that are structurally identical (like the unions and SumTypes in the above examples) are still considered separate types if their names are spelled differently. IMO the behavior of SumType in this regard is perfectly consistent with the rest of the language. If you want to use structural typing instead of nominal typing in your own code, you can define template predicates to check for SumTypes with a particular structure, the same way Phobos defines predicates like `isInputRange` to check for types with a particular structure.
Mar 18
next sibling parent reply Oleg B <code.viator gmail.com> writes:
On Thursday, 18 March 2021 at 18:09:38 UTC, Paul Backus wrote:
 union U1 { int n; float f; }
 union U2 { float f; int n; }

 static assert(is(U1 == U2)); // fails

 D is a nominally-typed language, not a structurally-typed one. 
 That means types that are structurally identical (like the 
 unions and SumTypes in the above examples) are still considered 
 separate types if their names are spelled differently.

 IMO the behavior of SumType in this regard is perfectly 
 consistent with the rest of the language. If you want to use 
 structural typing instead of nominal typing in your own code, 
 you can define template predicates to check for SumTypes with a 
 particular structure, the same way Phobos defines predicates 
 like `isInputRange` to check for types with a particular 
 structure.
okey, I agree but read-only tag (kind, type index) is key feature in chouse between sumtype and other for me, and I hope it will be added before next compiler release...
Mar 18
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 18:42:32 UTC, Oleg B wrote:
 but read-only tag (kind, type index) is key feature in chouse 
 between sumtype and other for me, and I hope it will be added 
 before next compiler release...
https://github.com/pbackus/sumtype/pull/57 https://github.com/dlang/phobos/pull/7886
Mar 18
parent Oleg B <code.viator gmail.com> writes:
On Friday, 19 March 2021 at 01:31:59 UTC, Paul Backus wrote:
 On Thursday, 18 March 2021 at 18:42:32 UTC, Oleg B wrote:
 but read-only tag (kind, type index) is key feature in chouse 
 between sumtype and other for me, and I hope it will be added 
 before next compiler release...
https://github.com/pbackus/sumtype/pull/57 https://github.com/dlang/phobos/pull/7886
Thank you very much!
Mar 19
prev sibling parent reply drug <drug2004 bk.ru> writes:
On 3/18/21 9:42 PM, Oleg B wrote:
 
 but read-only tag (kind, type index) is key feature in chouse between 
 sumtype and other for me, and I hope it will be added before next 
 compiler release...
 
Totally agree, it is an important feature.
Mar 19
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Friday, 19 March 2021 at 07:28:19 UTC, drug wrote:
 On 3/18/21 9:42 PM, Oleg B wrote:
 
 but read-only tag (kind, type index) is key feature in chouse 
 between sumtype and other for me, and I hope it will be added 
 before next compiler release...
 
Totally agree, it is an important feature.
https://github.com/pbackus/sumtype/pull/57 https://github.com/dlang/phobos/pull/7886
Mar 19
prev sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Friday, 19 March 2021 at 07:28:19 UTC, drug wrote:
 On 3/18/21 9:42 PM, Oleg B wrote:
 
 but read-only tag (kind, type index) is key feature in chouse 
 between sumtype and other for me, and I hope it will be added 
 before next compiler release...
 
Totally agree, it is an important feature.
Can anyone explain to me why?
Mar 23
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 3/23/21 10:45 AM, Atila Neves wrote:
 On Friday, 19 March 2021 at 07:28:19 UTC, drug wrote:
 On 3/18/21 9:42 PM, Oleg B wrote:
 but read-only tag (kind, type index) is key feature in chouse between 
 sumtype and other for me, and I hope it will be added before next 
 compiler release...
Totally agree, it is an important feature.
Can anyone explain to me why?
1. It costs nothing (returning tag is pretty much free). 2. reasoning about things to do with a SumType without having to generate (possibly) an entire set of handler functions allows different designs or writing code in more efficient/straightforward ways. Consider an assert that validates a SumType is a certain type: SumType!(string, float, int) someType; ... someType.match!((T t) {assert(is(T == int)); }); vs. assert(someType.typeIndex == 2); Which looks better? Which performs better? For the first case, all the types have to be used to build the lambda, which means 4 more functions, and the match call has to basically do a switch on all possible tags, and call the right function, which then throws an assert error or not. Or, without asserts enabled, builds 4 functions empty functions which hopefully are inlined. The second implementation checks if an integer equals 2. Or if asserts are disabled, elides the whole statement. Only drawback I see is it's very dependent on the type not changing indexes. All that said, the taggedalgebraic project looks nicer to me: union Values { string String; float Float; int Integer; } TaggedAlgebraic!(Values) algType; ... assert(algType.kind == algType.Kind.Integer); -Steve
Mar 23
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Tuesday, 23 March 2021 at 16:29:52 UTC, Steven Schveighoffer 
wrote:
 Consider an assert that validates a SumType is a certain type:

 SumType!(string, float, int) someType;
 ...
 someType.match!((T t) {assert(is(T == int)); });

 vs.

 assert(someType.typeIndex == 2);
Well, first, we would never write a naked magic number like this in production code, so for the sake of a fair comparison, let's fix that issue: assert(someType.typeIndex == staticIndexOf!(int, someType.Types)); Moving on--if I were doing this often enough for it to matter, I would define a helper function: bool contains(T, ST)(ST st) if (isSumType!ST) { return st.match!(value => is(typeof(value) == T)); } Usage: assert(someType.contains!int); Personally I think this is far preferable to the typeIndex version in terms of readability. In terms of performance: I checked the generated assembly, and `ldc -O` inlines everything, but `dmd -O -inline` does not, so if you are using DMD you're leaving performance on the table here (but isn't that true in general?). There is also some additional compile-time overhead from instantiating the templates.
Mar 23
prev sibling next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 23 March 2021 at 16:29:52 UTC, Steven Schveighoffer 
wrote:
 On 3/23/21 10:45 AM, Atila Neves wrote:
 On Friday, 19 March 2021 at 07:28:19 UTC, drug wrote:
 On 3/18/21 9:42 PM, Oleg B wrote:
 but read-only tag (kind, type index) is key feature in 
 chouse between sumtype and other for me, and I hope it will 
 be added before next compiler release...
Totally agree, it is an important feature.
Can anyone explain to me why?
1. It costs nothing (returning tag is pretty much free).
I don't think this is enough of a reason.
 2. reasoning about things to do with a SumType without having 
 to generate (possibly) an entire set of handler functions 
 allows different designs or writing code in more 
 efficient/straightforward ways.
This is the part I don't get. Why would one want to do that? To me this feels like checking for the dynamic type of an object in OOP, in the sense that if you're doing that you're probably doing something wrong.
 Consider an assert that validates a SumType is a certain type:

 SumType!(string, float, int) someType;
 ...
 someType.match!((T t) {assert(is(T == int)); });

 vs.

 assert(someType.typeIndex == 2);

 Which looks better?
The former.
 Which performs better?
It shouldn't matter - performance considerations in the absence of running a profiler are futile. If nobody notices, it doesn't matter. If someone noticed, a profiler gets run and the code optimised where it's actually slow. Having said that, I think that if there are two alternatives to getting the job done, and the two are equally readable/maintainable, prefer the fastest one. Or as Sutter/Alexandrescu once wrote, "belated pessimization is the root of no good". For the first case,
 all the types have to be used to build the lambda, which means 
 4 more functions, and the match call has to basically do a 
 switch on all possible tags, and call the right function, which 
 then throws an assert error or not. Or, without asserts 
 enabled, builds 4 functions empty functions which hopefully are 
 inlined.

 The second implementation checks if an integer equals 2. Or if 
 asserts are disabled, elides the whole statement. Only drawback 
 I see is it's very dependent on the type not changing indexes.

 All that said, the taggedalgebraic project looks nicer to me:

 union Values
 {
    string String;
    float Float;
    int Integer;
 }

 TaggedAlgebraic!(Values) algType;
 ...

 assert(algType.kind == algType.Kind.Integer);
If one wants to do that sort of thing, yes, this is nicer. I'm still at a loss as to why one would *want* to do such a thing. I think it's useful to remember that in languages where sum types and pattern matching are features there's no way to do this.
Mar 25
next sibling parent reply drug <drug2004 bk.ru> writes:
I think that `kind` vs `handler` is more like old-school vs modern 
style. They are equal in general. Modern compilers are good in 
optimization. I chose TaggedAlgebraic for my projects about 7 years ago 
and don't remember the exact reason I was need for kind feature. In 
general I agree to Steven. I can add to his post that kind lets you do:
```D
	switch(kind)
	{
	case Kind.foo0..case Kind.foo5:
		doSomething1;
		break;
	case Kind.bar:
	case Kind.baz:
	case Kind.foobar:
		doSomething2;
		break;
	default:
		doDefaultThings;
	}
```
using handlers:
```D
	someType.match!(
		(Foo0 foo) => doSomething1,
		(Foo1 foo) => doSomething1,
		(Foo2 foo) => doSomething1,
		(Foo3 foo) => doSomething1,
		(Foo4 foo) => doSomething1,
		(Bar  bar) => doSomething2,
		(Baz  baz) => doSomething2,
		(FooBar fb) => doSomething2,
		(_) => doDefaultThings;
	);
```
These examples are verbose equally but the first version contains less 
boilerplate.

I believe both approaches are good. They complement each other.
Mar 25
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 25 March 2021 at 15:03:37 UTC, drug wrote:
 In general I agree to Steven. I can add to his post that kind 
 lets you do:
 ```D
 	switch(kind)
 	{
 	case Kind.foo0..case Kind.foo5:
 		doSomething1;
 		break;
 	case Kind.bar:
 	case Kind.baz:
 	case Kind.foobar:
 		doSomething2;
 		break;
 	default:
 		doDefaultThings;
 	}
 ```
 using handlers:
 ```D
 	someType.match!(
 		(Foo0 foo) => doSomething1,
 		(Foo1 foo) => doSomething1,
 		(Foo2 foo) => doSomething1,
 		(Foo3 foo) => doSomething1,
 		(Foo4 foo) => doSomething1,
 		(Bar  bar) => doSomething2,
 		(Baz  baz) => doSomething2,
 		(FooBar fb) => doSomething2,
 		(_) => doDefaultThings;
 	);
 ```
 These examples are verbose equally but the first version 
 contains less boilerplate.
Only if you don't know what you're doing. Check this out: template restrictTo(Args...) if (Args.length >= 1) { import std.meta : IndexOf = staticIndexOf; alias Types = Args[0 .. $-1]; alias fun = Args[$]; auto ref restrictTo(T)(auto ref T value) if (IndexOf!(T, Types) >= 0); { import core.lifetime: forward static assert(IndexOf!(T, Types) >= 0); return fun(forward!value); } } Usage: someType.match!( restrictTo!(Foo0, Foo1, Foo2, Foo3, Foo4, val => doSomething1 ), restrictTo!(Bar, Baz, FooBar, val => doSomething2 ), _ => doDefaultThings );
Mar 25
next sibling parent drug <drug2004 bk.ru> writes:
On 3/25/21 6:17 PM, Paul Backus wrote:
 Usage:
 
      someType.match!(
          restrictTo!(Foo0, Foo1, Foo2, Foo3, Foo4,
              val => doSomething1
          ),
          restrictTo!(Bar, Baz, FooBar,
              val => doSomething2
          ),
          _ => doDefaultThings
      );
Looks good
Mar 25
prev sibling parent reply Jon Degenhardt <jond noreply.com> writes:
On Thursday, 25 March 2021 at 15:17:31 UTC, Paul Backus wrote:
 Only if you don't know what you're doing. Check this out:

     template restrictTo(Args...)
         if (Args.length >= 1)
     {
         import std.meta : IndexOf = staticIndexOf;

         alias Types = Args[0 .. $-1];
         alias fun = Args[$];

         auto ref restrictTo(T)(auto ref T value)
             if (IndexOf!(T, Types) >= 0);
         {
             import core.lifetime: forward

             static assert(IndexOf!(T, Types) >= 0);
             return fun(forward!value);
         }
     }

 Usage:

     someType.match!(
         restrictTo!(Foo0, Foo1, Foo2, Foo3, Foo4,
             val => doSomething1
         ),
         restrictTo!(Bar, Baz, FooBar,
             val => doSomething2
         ),
         _ => doDefaultThings
     );
This comment is not intended as criticism. SumType looks like a very good package. But, I am confused by this response. In particular, the expectation for when such a technique would be used. Use of D's case-range statement is not a particularly advanced technique. But writing the 'restrictTo' template requires a good bit more knowledge. I doubt the intent is that people will write the equivalent of 'restrictTo' whenever coming across a 'match' use like this. But I don't see 'restrictTo' included with SumType. Is the thought that this will be rare that it won't be needed? Or perhaps, that with additional experience, such a facility might be added to SumType later? Or something much more basic that I'm missing? Separate thing - I think the catch-all handler could be a bit better documented. Is the lack of a type that triggers it? Or is underscore ('_') special (ala Scala). If the latter, then are the '_1' and '_2' forms shown in the multiple dispatch examples special? (I'm guessing that there's nothing special about the underscore forms, but people familiar with Scala might assume some else.) I'm looking at the docs here: https://pbackus.github.io/sumtype/sumtype.SumType.html. --Jon
Mar 29
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 30 March 2021 at 03:13:04 UTC, Jon Degenhardt wrote:
 In particular, the expectation for when such a technique would 
 be used. Use of D's case-range statement is not a particularly 
 advanced technique. But writing the 'restrictTo' template 
 requires a good bit more knowledge. I doubt the intent is that 
 people will write the equivalent of 'restrictTo' whenever 
 coming across a 'match' use like this.
I wouldn't expect users to jump straight to the fully-generalized version, but you don't need a lot of fancy template machinery to get the same result in a specific case. For example: (val) { alias T = typeof(val); static assert(is(T == Foo0) || is(T == Foo1) || is(T == Foo2) || is(T == Foo3) || is(T == Foo4)); doSomething1; } And once you have that, it's not a huge leap to notice that you can clean it up a bit with `staticIndexOf`. And once you do that a few times, maybe you start to think about how to generalize it. I will grant that it may not be obvious that you can use `static assert` with `match` in this way to begin with. That's something that can be addressed by improving SumType's documentation.
 But I don't see 'restrictTo' included with SumType. Is the 
 thought that this will be rare that it won't be needed? Or 
 perhaps, that with additional experience, such a facility might 
 be added to SumType later? Or something much more basic that 
 I'm missing?
The thought is: I don't know in advance what will be common and what won't, so I think it's best to wait for feedback before adding facilities like this. In the long run, I expect we will see some utility templates like `restrictTo` added--either to the `sumtype` module, or to a more general module like `std.functional` if they stand on their own.
 Separate thing - I think the catch-all handler could be a bit 
 better documented. Is the lack of a type that triggers it? Or 
 is underscore ('_') special (ala Scala). If the latter, then 
 are the '_1' and '_2' forms shown in the multiple dispatch 
 examples special? (I'm guessing that there's nothing special 
 about the underscore forms, but people familiar with Scala 
 might assume some else.) I'm looking at the docs here: 
 https://pbackus.github.io/sumtype/sumtype.SumType.html.
Your guess that the underscore is not special is correct. This should be clear from reading the documentation for match [1], but I expect there are many users who read only the examples and skip the prose, so it would be best to add a note about this to the examples as well. [1] https://pbackus.github.io/sumtype/sumtype.match.html
Mar 30
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 3/30/21 12:32 PM, Paul Backus wrote:

 Separate thing - I think the catch-all handler could be a bit better 
 documented. Is the lack of a type that triggers it? Or is underscore 
 ('_') special (ala Scala). If the latter, then are the '_1' and '_2' 
 forms shown in the multiple dispatch examples special? (I'm guessing 
 that there's nothing special about the underscore forms, but people 
 familiar with Scala might assume some else.) I'm looking at the docs 
 here: https://pbackus.github.io/sumtype/sumtype.SumType.html.
Your guess that the underscore is not special is correct. This should be clear from reading the documentation for match [1], but I expect there are many users who read only the examples and skip the prose, so it would be best to add a note about this to the examples as well.
What might be nice is to have some simplifiers for match to prevent having to jump through the hoops. For example, a match flavor that throws by default, or one that ignores unhandled types. This way, you don't have to write stuff like `(_) {}` at the end of your handlers. FWIW, taggedalgebraic has `visit` to enforce all items are handled, and `tryVisit` that throws if at runtime it determines no visitors match the current type. -Steve
Mar 30
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 30 March 2021 at 17:01:29 UTC, Steven Schveighoffer 
wrote:
 What might be nice is to have some simplifiers for match to 
 prevent having to jump through the hoops.

 For example, a match flavor that throws by default, or one that 
 ignores unhandled types. This way, you don't have to write 
 stuff like `(_) {}` at the end of your handlers.

 FWIW, taggedalgebraic has `visit` to enforce all items are 
 handled, and `tryVisit` that throws if at runtime it determines 
 no visitors match the current type.
sumtype has had tryMatch [1] since version 0.4.0 (released May 2018). For ignoring types, I have found the following utility function handy: void ignore(T)(auto ref T) {} You can instantiate it explicitly to ignore a specific type: obj.match!( ignore!A, (B b) { doSomething1; }, (C c) { doSomething2; } ); Or you can use it as a catch-all handler: obj.match!( (A a) { doSomethingWith(a); }, ignore ); [1] https://pbackus.github.io/sumtype/sumtype.tryMatch.html
Mar 30
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 3/30/21 1:14 PM, Paul Backus wrote:
 On Tuesday, 30 March 2021 at 17:01:29 UTC, Steven Schveighoffer wrote:
 What might be nice is to have some simplifiers for match to prevent 
 having to jump through the hoops.

 For example, a match flavor that throws by default, or one that 
 ignores unhandled types. This way, you don't have to write stuff like 
 `(_) {}` at the end of your handlers.

 FWIW, taggedalgebraic has `visit` to enforce all items are handled, 
 and `tryVisit` that throws if at runtime it determines no visitors 
 match the current type.
sumtype has had tryMatch [1] since version 0.4.0 (released May 2018). For ignoring types, I have found the following utility function handy:    void ignore(T)(auto ref T) {} You can instantiate it explicitly to ignore a specific type:    obj.match!(        ignore!A,        (B b) { doSomething1; },        (C c) { doSomething2; }    ); Or you can use it as a catch-all handler:    obj.match!(        (A a) { doSomethingWith(a); },        ignore    ); [1] https://pbackus.github.io/sumtype/sumtype.tryMatch.html
Yeah, that actually looks pretty good. Forgive my ignorance, I have not looked at any depth at SumType. I think `ignore` would be a great addition to the library, and makes the code much easier to read. I can't think of a `match` name that would look better than just specifying ignore in the list. One thing that is sticking out via this mechanism for pattern matching is that with lambdas, you have to name the parameter (for everything but keywords) or you will not get what you are looking for. So things like (B b) { doSomething2; } would look nicer as (B) { doSomething2; } but obviously will not do what you would expect. Hence the usage of `_` in the previous examples. Not sure if there might be a language improvement to help with this. It probably is not that common anyway. -Steve
Mar 30
prev sibling parent reply Jon Degenhardt <jond noreply.com> writes:
On Tuesday, 30 March 2021 at 16:32:21 UTC, Paul Backus wrote:
 I will grant that it may not be obvious that you can use 
 `static assert` with `match` in this way to begin with. That's 
 something that can be addressed by improving SumType's 
 documentation.
Yeah, I would say its not obvious :) But, there does seem to be a fairly good opportunity for improving usability via documentation. As another example, the ability to define a catch-all handler is not mentioned in the text (or at least I couldn't find it). It's in the examples. I don't know that I'll have time to propose doc updates via pull-request, but I might have time to make suggestions by some other mechanism, like issue reports something else. Is there a mechanism/venue that would work for you? --Jon
Mar 31
parent Paul Backus <snarwin gmail.com> writes:
On Wednesday, 31 March 2021 at 17:34:00 UTC, Jon Degenhardt wrote:
 As another example, the ability to define a catch-all handler 
 is not mentioned in the text (or at least I couldn't find it). 
 It's in the examples.
Well, it's not really a specific "ability"; it's just a natural consequence of the fact that you're allowed to use templates as handlers. I think an example is the most appropriate place to illustrate something like that, but it is probably worth adding a link to the documentation to explicitly point out the connection.
 I don't know that I'll have time to propose doc updates via 
 pull-request, but I might have time to make suggestions by some 
 other mechanism, like issue reports something else. Is there a 
 mechanism/venue that would work for you?
If you want to make sure that it's seen by me specifically, the easiest way is probably to submit an issue to the `sumtype` repository on Github [1]. Another possibility is to submit a report to issues.dlang.org and add me to the CC list. I have not tried this myself, but I expect Bugzilla would notify me. [1] https://github.com/pbackus/sumtype/issues/
Mar 31
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 25 March 2021 at 14:28:06 UTC, Atila Neves wrote:
 If one wants to do that sort of thing, yes, this is nicer. I'm 
 still at a loss as to why one would *want* to do such a thing.

 I think it's useful to remember that in languages where sum 
 types and pattern matching are features there's no way to do 
 this.
I suspect the big reason is that a lot of D programmers aren't super familiar with these languages, or with functional programming in general, so they find SumType's match function less comfortable and approachable than switching on an enum (even though it's fundamentally the same thing). There's a great illustration of this in a forum thread from last year [1]. Once I showed how to convert the code to use SumType and match [2], the author could understand it, but they weren't able to come up with it on their own. [1] https://forum.dlang.org/post/kjozzeynentyrarssnzz forum.dlang.org [2] https://forum.dlang.org/post/bzueywubiazbkxvqnaid forum.dlang.org
Mar 25
parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 25 March 2021 at 15:05:40 UTC, Paul Backus wrote:
 On Thursday, 25 March 2021 at 14:28:06 UTC, Atila Neves wrote:
 I think it's useful to remember that in languages where sum 
 types and pattern matching are features there's no way to do 
 this.
I suspect the big reason is that a lot of D programmers aren't super familiar with these languages, or with functional programming in general, so they find SumType's match function less comfortable and approachable
Completely plausible but in my opinion not nearly enough of a reason to offer an escape hatch.
 than switching on an enum (even though it's fundamentally the 
 same thing).
Matching is like *final* switching on the enum, except that's that the only thing you can do so you can't screw up.
Mar 25
parent Paul Backus <snarwin gmail.com> writes:
On Friday, 26 March 2021 at 02:56:29 UTC, Atila Neves wrote:
 Completely plausible but in my opinion not nearly enough of a 
 reason to offer an escape hatch.
IMO typeIndex is not really an "escape hatch", because while you can write: if (sumTypeInstance.typeIndex == 2) { // ... } ...there is still nothing you can do inside the `if` body to access the value without going through `match`. To follow the OOP analogy, it would be like having access to `instanceof` but not downcasts--you can check what the runtime type is, but you still need to go through the normal runtime dispatch mechanism to do anything with the object. I think this principle--that you can't touch the SumType's value without using `match`--is a good place to draw the line on what we're willing to allow in SumType's public API and what we're not, but I am happy to hear arguments for other positions. Perhaps we can talk about this during the upcoming BeerConf.
Mar 26
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 3/25/21 10:28 AM, Atila Neves wrote:
 On Tuesday, 23 March 2021 at 16:29:52 UTC, Steven Schveighoffer wrote:
 On 3/23/21 10:45 AM, Atila Neves wrote:
 On Friday, 19 March 2021 at 07:28:19 UTC, drug wrote:
 On 3/18/21 9:42 PM, Oleg B wrote:
 but read-only tag (kind, type index) is key feature in chouse 
 between sumtype and other for me, and I hope it will be added 
 before next compiler release...
Totally agree, it is an important feature.
Can anyone explain to me why?
1. It costs nothing (returning tag is pretty much free).
I don't think this is enough of a reason.
But it is a reason. If it costs nothing to return it, and doesn't HARM anything to return it, why not make it available? A tagged union is required to store the tag, so it's not like that piece of data will cease to be available.
 
 2. reasoning about things to do with a SumType without having to 
 generate (possibly) an entire set of handler functions allows 
 different designs or writing code in more efficient/straightforward ways.
This is the part I don't get. Why would one want to do that? To me this feels like checking for the dynamic type of an object in OOP, in the sense that if you're doing that you're probably doing something wrong.
I'm not sure if you have considered that `match` actually does exactly what you are saying not to do. In fact it has to. `SumType` has a dynamic type. It's just easier to write code sometimes if you have the primitives to do it.
 
 Consider an assert that validates a SumType is a certain type:

 SumType!(string, float, int) someType;
 ...
 someType.match!((T t) {assert(is(T == int)); });

 vs.

 assert(someType.typeIndex == 2);

 Which looks better?
The former.
We disagree on that.
 
 Which performs better?
It shouldn't matter - performance considerations in the absence of running a profiler are futile. If nobody notices, it doesn't matter. If someone noticed, a profiler gets run and the code optimised where it's actually slow.
I'm not talking about runtime performance.
 Having said that, I think that if there are two alternatives to getting 
 the job done, and the two are equally readable/maintainable, prefer the 
 fastest one. Or as Sutter/Alexandrescu once wrote, "belated 
 pessimization is the root of no good".
I don't need a profiler to tell me that checking an integer against 2 is easier for the compiler to deal with than: 1. static-foreaching over a list of templates 2. Seeing which templates match which types, which also have to be static-foreached over 3. Determining if the lambda functions can be inlined 4. Inlining said functions 5. Optimizing out the combination of all the instantiated functions into "check this integer against 2".
 The second implementation checks if an integer equals 2. Or if asserts 
 are disabled, elides the whole statement. Only drawback I see is it's 
 very dependent on the type not changing indexes.

 All that said, the taggedalgebraic project looks nicer to me:

 union Values
 {
    string String;
    float Float;
    int Integer;
 }

 TaggedAlgebraic!(Values) algType;
 ...

 assert(algType.kind == algType.Kind.Integer);
If one wants to do that sort of thing, yes, this is nicer. I'm still at a loss as to why one would *want* to do such a thing.
I see code like this all the time: static if(is(T == int)) { ... } This directly maps to what one needs to do here. You are checking the type of a dynamic type against one of the possibilities (which necessarily is done at runtime). If for example, I want to only execute some code if the type is an int, then I still have to handle all the other types if I use `match`. The code "if it's an int then execute this code" reads so much better than "for all types in the sumtype, check if the type is an int, and if so, execute this code, and if not, execute code that does nothing." The only awkward part is using the literal `2`, which probably I would extract down to using a statcIndexOf (which I don't really like, because more meaningless work for the compiler).
 I think it's useful to remember that in languages where sum types and 
 pattern matching are features there's no way to do this.
In swift, for instance, you can use switch on a type. Something like that built into D would allow this to be much more palatable, but would probably require first-class types. ``` switch(sumtype.type) { case int v: doMyCode(v); break; default: break; } ``` But I still don't love it. If SumType were a compiler builtin, it would be much more appealing (then you aren't involving all sorts of template machinery to figure out which code to run, and hope the compiler optimizes it out). The fact that the tag is there, but you can't access it is.... puzzling. It's OK, taggedalgebraic exists, so I can just use that instead of SumType. Hopefully std.sumtype doesn't become a rarely used module, but I'll stay away from it for now. -Steve
Mar 29
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Tuesday, 23 March 2021 at 16:29:52 UTC, Steven Schveighoffer 
wrote:
 Consider an assert that validates a SumType is a certain type:

 SumType!(string, float, int) someType;
 ...
 someType.match!((T t) {assert(is(T == int)); });

 vs.

 assert(someType.typeIndex == 2);
How about e.g.: assert(someType.has!int); int i = someType.unwrap!int; // asserts has!int
Mar 31
prev sibling parent Oleg B <code.viator gmail.com> writes:
On Tuesday, 23 March 2021 at 14:45:18 UTC, Atila Neves wrote:
 On Friday, 19 March 2021 at 07:28:19 UTC, drug wrote:
 On 3/18/21 9:42 PM, Oleg B wrote:
 
 but read-only tag (kind, type index) is key feature in chouse 
 between sumtype and other for me, and I hope it will be added 
 before next compiler release...
 
Totally agree, it is an important feature.
Can anyone explain to me why?
Simple general example is serialization and deserialization of algebraic value: without tag (kind, type index) it's not impossible but more complicated. For serialization you need write tag first (`match` is good for serialization exist value) and then deserialize you need to read tag and do deserialization for writed value type (how it must be with `match`?). If tag is private and/or it not direct point to types you need "jump through hoops" for this simple algo.
Mar 31
prev sibling parent Q. Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 18 March 2021 at 18:09:38 UTC, Paul Backus wrote:
 On Thursday, 18 March 2021 at 17:24:13 UTC, Oleg B wrote:
 May be depending on order of types must be fixed too

 ```
 alias S1 = Sumtype!(int, float);
 alias S2 = Sumtype!(float, int);

 static assert (is(S1 == S2)); // false by now
 ```
 because no different between S1 and S2 in practice use
union U1 { int n; float f; } union U2 { float f; int n; } static assert(is(U1 == U2)); // fails
Even union U1 { int n; float f; } union U2 { int n; float f; } static assert(is(U1 == U2)); // also fails If types have different names, they're considered different. It's that simple. S1 and S2 shouldn't be considered equal IMO.
Mar 18
prev sibling next sibling parent reply ryuukk_ <ryuukk_ gmail.com> writes:
I'm not a fan of the syntax.. why is it a library instead of a 
language feature?..

```
Fahrenheit toFahrenheit(Temperature t)
{
     return Fahrenheit(
         t.match!(
             (Fahrenheit f) => f.degrees,
             (Celsius c) => c.degrees * 9.0/5 + 32,
             (Kelvin k) => k.degrees * 9.0/5 - 459.4
         )
     );
}
```

vs

```
Fahrenheit toFahrenheit(Temperature t)
{
     return match(t) {
             (Fahrenheit f) => f.degrees,
             (Celsius c) => c.degrees * 9.0/5 + 32,
             (Kelvin k) => k.degrees * 9.0/5 - 459.4
     };
}
```

I don't understand why make things confusing when it can be done 
simpler

Discriminated union should be a language feature, not lib!

Because now you depend on ``std``
Mar 18
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 18:06:45 UTC, ryuukk_ wrote:
 I'm not a fan of the syntax.. why is it a library instead of a 
 language feature?..
Because it's easier to write a library than to go through the whole process of writing a DIP for a new language feature, getting it accepted, and implementing it in the compiler. If you or anyone else wants to write a DIP to add sum types to D as a language feature, I will happily support the effort, but I am not going to do it myself.
 Because now you depend on ``std``
`std.sumtype` has no non-optional dependencies on Phobos outside of compile time, so you can use it even if you do not want to depend on anything else in `std`.
Mar 18
next sibling parent reply ryuukk_ <ryuukk_ gmail.com> writes:
On Thursday, 18 March 2021 at 18:15:29 UTC, Paul Backus wrote:
 On Thursday, 18 March 2021 at 18:06:45 UTC, ryuukk_ wrote:
 I'm not a fan of the syntax.. why is it a library instead of a 
 language feature?..
Because it's easier to write a library than to go through the whole process of writing a DIP for a new language feature, getting it accepted, and implementing it in the compiler. If you or anyone else wants to write a DIP to add sum types to D as a language feature, I will happily support the effort, but I am not going to do it myself.
 Because now you depend on ``std``
`std.sumtype` has no non-optional dependencies on Phobos outside of compile time, so you can use it even if you do not want to depend on anything else in `std`.
That's how i feel about it, people didn't care enough and easiest solution picked (let's put this lib instead), now you have to use a feature, as a lib, calling functions that looks like template functions, in the end it is confusing i can switch(t), but i can't match(t), i need to use a different syntax
Mar 18
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 18:29:34 UTC, ryuukk_ wrote:
 That's how i feel about it, people didn't care enough and 
 easiest solution picked (let's put this lib instead), now you 
 have to use a feature, as a lib, calling functions that looks 
 like template functions, in the end it is confusing

 i can switch(t), but i can't match(t), i need to use a 
 different syntax
I cared enough to spend my free time for the last *three years* working on the sumtype library and submitting it for inclusion in Phobos. That's more than you've done, I'll bet. I understand your frustration, but these kinds of disparaging comments are extremely disrespectful, and you should avoid making them if you want anyone to take your criticisms seriously.
Mar 18
parent ryuukk_ <ryuukk_ gmail.com> writes:
On Thursday, 18 March 2021 at 18:40:01 UTC, Paul Backus wrote:
 On Thursday, 18 March 2021 at 18:29:34 UTC, ryuukk_ wrote:
 That's how i feel about it, people didn't care enough and 
 easiest solution picked (let's put this lib instead), now you 
 have to use a feature, as a lib, calling functions that looks 
 like template functions, in the end it is confusing

 i can switch(t), but i can't match(t), i need to use a 
 different syntax
I cared enough to spend my free time for the last *three years* working on the sumtype library and submitting it for inclusion in Phobos. That's more than you've done, I'll bet. I understand your frustration, but these kinds of disparaging comments are extremely disrespectful, and you should avoid making them if you want anyone to take your criticisms seriously.
I apologies if i sounded rude or disrespectful, that wasn't my intention As i said in the other reply, sumtype as a library is excellent, sumtype in the std on other than is where i disagree, if inclusion, that would be as a language feature, to be useful for everyone, and to enhance the syntax for everyone
Mar 18
prev sibling parent reply ryuukk_ <ryuukk_ gmail.com> writes:
On Thursday, 18 March 2021 at 18:15:29 UTC, Paul Backus wrote:
 On Thursday, 18 March 2021 at 18:06:45 UTC, ryuukk_ wrote:
 I'm not a fan of the syntax.. why is it a library instead of a 
 language feature?..
Because it's easier to write a library than to go through the whole process of writing a DIP for a new language feature, getting it accepted, and implementing it in the compiler. If you or anyone else wants to write a DIP to add sum types to D as a language feature, I will happily support the effort, but I am not going to do it myself.
The syntax is a result of it not being a language feature (the lib itself is perfectly fine, don't get me wrong! but the problem i have is it'd be better as a language feature) I'd love to help create a DIP for it, but making it as a lib now, and then removing it few months/year later will feel bad because that's gonna be code people will have to change, again, hence the importance of not taking shortcuts, we need think long term
Mar 18
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Thursday, 18 March 2021 at 18:40:49 UTC, ryuukk_ wrote:
 I'd love to help create a DIP for it, but making it as a lib 
 now, and then removing it few months/year later will feel bad 
 because that's gonna be code people will have to change, again, 
 hence the importance of not taking shortcuts, we need think 
 long term
There is no need to remove the library even if a built-in version is added. The library version can simply be updated to alias itself to the built-in version, and its documentation can be hidden to discourage new code from using it.
Mar 18
prev sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Thursday, 18 March 2021 at 18:40:49 UTC, ryuukk_ wrote:
 On Thursday, 18 March 2021 at 18:15:29 UTC, Paul Backus wrote:
[...]
The syntax is a result of it not being a language feature (the lib itself is perfectly fine, don't get me wrong! but the problem i have is it'd be better as a language feature) I'd love to help create a DIP for it, but making it as a lib now, and then removing it few months/year later will feel bad because that's gonna be code people will have to change, again, hence the importance of not taking shortcuts, we need think long term
Instead of spending three years on the DIP, start small and spend three days and see how far you come.
Mar 23
prev sibling parent kumar0raja <kumar0raja9 gmail.com> writes:
I agree that the process for adding new modules to Phobos could 
do with an overhaul, and that the community should be involved in 
some way, as it is for DIPs.
Mar 29
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 3/18/21 12:11 PM, Paul Backus wrote:
 On Thursday, 18 March 2021 at 15:58:55 UTC, Oleg B wrote:
 auto tag = val.match!(
   (Type1 t1) => 0, // or MyOwnValTag.type1
   (Type2 t2) => 1, // or MyOwnValTag.type2
   ... etc
 );

 next write tag, in next match write value

 how to generate this match without mixins? how to get (in general way) 
 type from sumtype val by own tag or number for deserialization?
Something like this: alias QualifiedTypes = CopyTypeQualifiers!(typeof(val), val.Types); auto tag = val.match!(v => staticIndexOf!(typeof(v), QualifiedTypes));
Wait, you can't access the tag for the tagged union? FYI, taggedalgebraic, it's simply val.kind. I'd suggest giving read-only access to the tag. The types are already accessible. -Steve
Mar 18
prev sibling parent reply Oleg B <code.viator gmail.com> writes:
Paul, have you any plans to update `std.sumtype` to actual 
version of `sumtype` (1.1.x)?

`typeIndex` is needed
Aug 29
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 29 August 2021 at 14:00:30 UTC, Oleg B wrote:
 Paul, have you any plans to update `std.sumtype` to actual 
 version of `sumtype` (1.1.x)?

 `typeIndex` is needed
I did. The change was reverted by Atila Neves: https://github.com/dlang/phobos/pull/7922 He was not satisfied with the rationale given in this thread for adding `typeIndex`. Since you use it yourself, you can probably give a better example use-case than I did; feel free to add your own comment to the discussion.
Aug 29
parent reply Oleg B <code.viator gmail.com> writes:
On Sunday, 29 August 2021 at 14:25:11 UTC, Paul Backus wrote:
 On Sunday, 29 August 2021 at 14:00:30 UTC, Oleg B wrote:
 Paul, have you any plans to update `std.sumtype` to actual 
 version of `sumtype` (1.1.x)?

 `typeIndex` is needed
I did. The change was reverted by Atila Neves: https://github.com/dlang/phobos/pull/7922 He was not satisfied with the rationale given in this thread for adding `typeIndex`. Since you use it yourself, you can probably give a better example use-case than I did; feel free to add your own comment to the discussion.
I write `sbin` and here I try to rewriter for use `std.sumtype` https://github.com/deviator/sbin/blob/master/source/sbin/type.d#L147 like this ```d static if (isTagged!(T).isSumType) { version (Have_std_sumtype) { import std : staticIndexOf; // version from std doesn't have typeIndex return cast(TaggedTagType!T)( sumtype.match!(v => staticIndexOf!(Unqual!(typeof(v)), T.Types))(val)); } else return cast(TaggedTagType!(T))(val.typeIndex); } ``` but on this code I get problem: ```d alias UT3 = SumType!(typeof(null), byte, This[]); unittest { auto val1 = UT3([ UT3(42), UT3(null), UT3([ UT3(null), UT3(65) ]), UT3(12) ]); import std : stderr; auto data = sbinSerialize(val1); stderr.writeln(data); stderr.writeln([2, 4, 1, 42, 0, 2, 2, 0, 1, 65, 1, 12]); assert (data == [2, 4, 1, 42, 0, 2, 2, 0, 1, 65, 1, 12]); auto val2 = sbinDeserialize!UT3(data); assert (val1 == val2); } ``` output: ``` [255, 4, 1, 42, 0, 255, 2, 0, 1, 65, 1, 12] [2, 4, 1, 42, 0, 2, 2, 0, 1, 65, 1, 12] ... assertion ... ``` It means that `sumtype.match!(v => staticIndexOf!(Unqual!(typeof(v)), T.Types))(val));` doesn't return true value of type index (255 instead of 2). Can you suggest how to rewrite it? If it not possible, may be it can be strong argument for adding `typeIndex`? ps sumtype 1.1.4 with `typeIndex` works perfect with `UT3`.
Aug 29
parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 29 August 2021 at 16:04:53 UTC, Oleg B wrote:
 It means that `sumtype.match!(v => 
 staticIndexOf!(Unqual!(typeof(v)), T.Types))(val));` doesn't 
 return true value of type index (255 instead of 2).
`staticIndexOf` returns `-1` when the type is not found, and `255` is what you get when you cast `-1` to `ubyte`.
 Can you suggest how to rewrite it? If it not possible, may be 
 it can be strong argument for adding `typeIndex`?
Sure. Here's the implementation of `typeIndex` using `match`: ```d /** * Returns the index of the type of the `SumType`'s current value in the * `SumType`'s `Types`. * * If the `SumType` is qualified, returns the index of the type in `Types` * whose qualified version matches the `SumType`'s current value. */ size_t typeIndex(This)(auto ref This this_) { import std.traits : CopyTypeQualifiers; import std.meta : IndexOf = staticIndexOf; alias Qualify(T) = CopyTypeQualifiers!(This, T); alias QualifiedTypes = Map!(Qualify, This.Types); return this_.match!((ref value) => IndexOf!(typeof(value), QualifiedTypes) ); } ``` Note that this version has [the problem you complained about before][1] where it does not distinguish between the `int[]` and the `const(int[])` members of a `const(SumType!(int[], const(int[])))`. This is unavoidable--`match` itself is not capable of distinguishing between those members, so anything that uses `match` will also fail to do so. If this is still a problem for you, you can strip off the qualifier from the `SumType` before calling `typeIndex`: ```d return cast(TaggedTagType!(T))((cast(Unqual!T) val).typeIndex); ``` An unqualified `SumType` can never have duplicate member types, so this will always give consistent results. [1]: https://github.com/pbackus/sumtype/issues/58
Aug 29
parent reply Oleg B <code.viator gmail.com> writes:
On Sunday, 29 August 2021 at 17:49:05 UTC, Paul Backus wrote:
 `staticIndexOf` returns `-1` when the type is not found, and 
 `255` is what you get when you cast `-1` to `ubyte`.
Yes, I understand, but it wasn't problem.
 Note that this version has [the problem you complained about 
 before][1] where it does not distinguish between the `int[]` 
 and the `const(int[])` members of a `const(SumType!(int[], 
 const(int[])))`. This is unavoidable--`match` itself is not 
 capable of distinguishing between those members, so anything 
 that uses `match` will also fail to do so.

 If this is still a problem for you, you can strip off the 
 qualifier from the `SumType` before calling `typeIndex`:

 ```d
         return cast(TaggedTagType!(T))((cast(Unqual!T) 
 val).typeIndex);
 ```
No problem with `typeIndex`, but `std.sumtype` have no `typeIndex`. If I understand correctly problem must resolve by removing `const` before `match`. Is this code will be correct? ```d auto typeIndex(T)(in T val) if (is(T == SumType!Args, Args...)) { return match!(v => staticIndexOf!(typeof(v), T.Types))(*(cast(T*)&val)); } ``` In what cases I get different result between your version with `CopyTypeQualifiers` and my?
Aug 30
parent reply drug <drug2004 bk.ru> writes:
30.08.2021 13:20, Oleg B пишет:
 
 ```d
 auto typeIndex(T)(in T val) if (is(T == SumType!Args, Args...))
 {
    return match!(v => staticIndexOf!(typeof(v), T.Types))(*(cast(T*)&val));
 }
 ```
A little tip: you can use just `cast()` to remove qualifiers: ```D return match!(v => staticIndexOf!(typeof(v), T.Types))(cast()val)); ```
Aug 30
parent reply Oleg B <code.viator gmail.com> writes:
On Monday, 30 August 2021 at 10:50:40 UTC, drug wrote:
 A little tip: you can use just `cast()` to remove qualifiers:
 ```D
 return match!(v => staticIndexOf!(typeof(v), 
 T.Types))(cast()val));
 ```
I'm not sure what with simple `cast()` not `opCast` nor ctors are called, it depends on code of `SumType`, if it changed they can be called (not sure). Cast through pointer avoid it. Or I mistake?
Aug 30
parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 30 August 2021 at 11:19:46 UTC, Oleg B wrote:
 On Monday, 30 August 2021 at 10:50:40 UTC, drug wrote:
 A little tip: you can use just `cast()` to remove qualifiers:
 ```D
 return match!(v => staticIndexOf!(typeof(v), 
 T.Types))(cast()val));
 ```
I'm not sure what with simple `cast()` not `opCast` nor ctors are called, it depends on code of `SumType`, if it changed they can be called (not sure). Cast through pointer avoid it. Or I mistake?
`SumType` does not define any `opCast` overloads, so you don't have to worry about that. Re: ctors/copying, the [language spec for casts][1] says:
 10. Casting to a CastQual replaces the qualifiers to the type 
 of the UnaryExpression.

 11. Casting with no Type or CastQual removes any top level 
 const, immutable, shared or inout type modifiers from the type 
 of the UnaryExpression.
In other words, casting to a different qualifier does not create a new value; it just causes the compiler to treat the existing value as though it had a different type. [1]: https://dlang.org/spec/expression.html#CastExpression
Aug 30
parent Oleg B <code.viator gmail.com> writes:
On Monday, 30 August 2021 at 12:13:56 UTC, Paul Backus wrote:
 On Monday, 30 August 2021 at 11:19:46 UTC, Oleg B wrote:
 On Monday, 30 August 2021 at 10:50:40 UTC, drug wrote:
 A little tip: you can use just `cast()` to remove qualifiers:
 ```D
 return match!(v => staticIndexOf!(typeof(v), 
 T.Types))(cast()val));
 ```
I'm not sure what with simple `cast()` not `opCast` nor ctors are called, it depends on code of `SumType`, if it changed they can be called (not sure). Cast through pointer avoid it. Or I mistake?
`SumType` does not define any `opCast` overloads, so you don't have to worry about that. Re: ctors/copying, the [language spec for casts][1] says:
 10. Casting to a CastQual replaces the qualifiers to the type 
 of the UnaryExpression.

 11. Casting with no Type or CastQual removes any top level 
 const, immutable, shared or inout type modifiers from the type 
 of the UnaryExpression.
In other words, casting to a different qualifier does not create a new value; it just causes the compiler to treat the existing value as though it had a different type. [1]: https://dlang.org/spec/expression.html#CastExpression
Thanks!
Aug 30
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/29/21 10:00 AM, Oleg B wrote:
 Paul, have you any plans to update `std.sumtype` to actual version of 
 `sumtype` (1.1.x)?
 
 `typeIndex` is needed
It was [added](https://github.com/dlang/phobos/pull/7886), and then [removed](https://github.com/dlang/phobos/pull/7922) before release by Atila. I suspect it will never be re-added. -Steve
Aug 29
parent reply SealabJaster <sealabjaster gmail.com> writes:
On Sunday, 29 August 2021 at 14:29:09 UTC, Steven Schveighoffer 
wrote:
 ...
What a shame... I've been using std.sumtype recently and I was really hoping something like this would be added in. It feels really clunky otherwise.
Aug 29
parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 29 August 2021 at 14:45:20 UTC, SealabJaster wrote:
 On Sunday, 29 August 2021 at 14:29:09 UTC, Steven Schveighoffer 
 wrote:
 ...
What a shame... I've been using std.sumtype recently and I was really hoping something like this would be added in. It feels really clunky otherwise.
If you find the lack of `typeIndex` is leading to clunky code, you are probably not taking full advantage of `SumType`'s existing capabilities. In particular, `SumType` strongly rewards code written in a "functional" style, where different parts of a program are coupled together explicitly via function arguments and return values rather than implicitly via shared state and mutation. For a concrete example, have a look at * [Simen Kjærås's imperative-style code using a tagged union.][1] * [My functional-style version using `SumType`.][2] If you can give an example of the kind of code you have in mind, I would be happy to give more specific advice. [1]: https://forum.dlang.org/post/kjozzeynentyrarssnzz forum.dlang.org [2]: https://forum.dlang.org/post/bzueywubiazbkxvqnaid forum.dlang.org
Aug 29
parent reply SealabJaster <sealabjaster gmail.com> writes:
On Sunday, 29 August 2021 at 15:05:57 UTC, Paul Backus wrote:
 ...
It is also of course totally possible I'm not using sumtypes right, but I'm frequently in situations such as this: ``` private string expectSingleIdentifier(Token tok, Expression[] exp) { string ret; exp[0].match!( (IdentifierExpression exp) { ret = exp.ident; }, (_) { throw new Exception(...); } ); return ret; } ``` While in this case I still have to go through `match` in order to access the value, sometimes I simply want to do a type check, and going through `match` seems a bit overkill. I guess it's just a niche case (wanting to 'abort' on a bad value rather than keep going) I keep running into, likely a sign I need to change my mindset rather than anything else. Or I'm just using it as a wrong abstraction >;3 In this specific case as well I don't believe I can use `return match!(...)` because I can't return anything in the "bad" case, since I'd then get an unreachable code error. Very minor yet annoying thing, but that's more a language issue rather than something inherent to SumType. And I keep forgetting to make the difference between a tagged union and a sum type, which is likely muddying my opinion since it's easy to extract and test values with the former case.
Aug 29
next sibling parent reply SealabJaster <sealabjaster gmail.com> writes:
On Sunday, 29 August 2021 at 19:19:54 UTC, SealabJaster wrote:
 ...
While I'm on a tiny rant, I'd like to just mention that compiler errors with SumType can be a bit obtuse. It's often annoying when you're using a generic case but due to a compiler error that the SumType eats up, all you get is "handler[x] is never matched" or "type X is unhandled", etc. Usually all you have to do is make a specific case for the type(s) causing the issue, but it's just a bit of extra work I'd rather not do. I appreciate that it's not really possible/easy to distinguish the case of "this handler *does* handle this type, it just doesn't compile due to an error in the body" though.
Aug 29
parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 29 August 2021 at 19:29:29 UTC, SealabJaster wrote:
 On Sunday, 29 August 2021 at 19:19:54 UTC, SealabJaster wrote:
 ...
While I'm on a tiny rant, I'd like to just mention that compiler errors with SumType can be a bit obtuse. It's often annoying when you're using a generic case but due to a compiler error that the SumType eats up, all you get is "handler[x] is never matched" or "type X is unhandled", etc.
I agree, this is annoying. My current best solution to this is: 1. Recompile with `-verrors=spec` and redirect the compiler's output to a file. 2. `grep` the file for error messages whose location is in one of my source files. 3. Search the results of that `grep` for the actual error that caused things to fail. By the way, this process works for anything that fails because of errors in a `__traits(compiles)` or `typeof(...)` check, not just `match`.
Aug 29
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 29 August 2021 at 19:19:54 UTC, SealabJaster wrote:
 On Sunday, 29 August 2021 at 15:05:57 UTC, Paul Backus wrote:
 ...
It is also of course totally possible I'm not using sumtypes right, but I'm frequently in situations such as this: ``` private string expectSingleIdentifier(Token tok, Expression[] exp) { string ret; exp[0].match!( (IdentifierExpression exp) { ret = exp.ident; }, (_) { throw new Exception(...); } ); return ret; } ```
For cases like this you can use `tryMatch`, which does not require exhaustiveness and throws an exception at runtime if it encounters a type with no handler: ```d return exp[0].tryMatch!( (IdentifierExpression exp) => exp.ident ); ```
 While in this case I still have to go through `match` in order 
 to access the value, sometimes I simply want to do a type 
 check, and going through `match` seems a bit overkill.

 I guess it's just a niche case (wanting to 'abort' on a bad 
 value rather than keep going) I keep running into, likely a 
 sign I need to change my mindset rather than anything else.

 Or I'm just using it as a wrong abstraction >;3
If "abort unless this `SumType` contains a particular type" is a common pattern in your code I would suggest extracting it into a helper function: ```d void require(T, S)(S s) if (isSumType!S) { s.match!( (T t) {} (_) { assert(0); } ); } ``` But in general, I think this is the wrong approach. What I would do instead is create a helper function like the following: ```d Optional!T maybeGet(T, S)(S s) if (isSumType!S) { return s.match!( (T t) => some(t), _ => no!T; ); } ``` This narrows the set of cases you have to deal with from "anything that could be in the `SumType`" to "either `T` or not `T`", and from there you can handle the "not `T`" case however you want--abort, throw an exception, etc. The general principle here is ["Parse, don't validate."][1] [1]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
 In this specific case as well I don't believe I can use `return 
 match!(...)` because I can't return anything in the "bad" case, 
 since I'd then get an unreachable code error. Very minor yet 
 annoying thing, but that's more a language issue rather than 
 something inherent to SumType.
There is actually a workaround for this: ```d return exp[0].match!( (IdentifierExpression exp) => exp.ident, function string (_) { throw new Exception("..."); } ); ``` Note the explicit return type of `string` on the second lambda. The compiler will allow this, even though the lambda never returns a `string`, because it never returns normally at all.
Aug 29
next sibling parent SealabJaster <sealabjaster gmail.com> writes:
On Sunday, 29 August 2021 at 19:51:30 UTC, Paul Backus wrote:
 ```d
 return exp[0].tryMatch!(
     (IdentifierExpression exp) => exp.ident
 );
 ```
That's pretty much perfect, thanks!
 But in general, I think this is the wrong approach. What I 
 would do instead is create a helper function like the following:

 ```d
 Optional!T maybeGet(T, S)(S s)
     if (isSumType!S)
 {
     return s.match!(
         (T t) => some(t),
         _ => no!T;
     );
 }
 ```

 This narrows the set of cases you have to deal with from 
 "anything that could be in the `SumType`" to "either `T` or not 
 `T`", and from there you can handle the "not `T`" case however 
 you want--abort, throw an exception, etc.

 The general principle here is ["Parse, don't validate."][1]
An interesting way to think about things, which I assume is from your apparent experience with functional languages? There's definitely a lot to learn from them, even with my limited
 ```d
 return exp[0].match!(
     (IdentifierExpression exp) => exp.ident,
     function string (_) { throw new Exception("..."); }
 );
 ```
I didn't even realise that syntax existed, TIL.
 I agree, this is annoying. My current best solution to this is:
Glad it's not just me, another reason we need this: https://forum.dlang.org/post/rj5hok$c6q$1 digitalmars.com Thanks for your thorough answers.
Aug 29
prev sibling next sibling parent surlymoor <surlymoor cock.li> writes:
On Sunday, 29 August 2021 at 19:51:30 UTC, Paul Backus wrote:
 ```d
 return exp[0].match!(
     (IdentifierExpression exp) => exp.ident,
     function string (_) { throw new Exception("..."); }
 );
 ```

 Note the explicit return type of `string` on the second lambda. 
 The compiler will allow this, even though the lambda never 
 returns a `string`, because it never returns normally at all.
Can't wait until DIP1034 is fully implemented so one needn't do this.
Aug 29
prev sibling parent JG <JG somewhere.com> writes:
On Sunday, 29 August 2021 at 19:51:30 UTC, Paul Backus wrote:
 On Sunday, 29 August 2021 at 19:19:54 UTC, SealabJaster wrote:
 [...]
For cases like this you can use `tryMatch`, which does not require exhaustiveness and throws an exception at runtime if it encounters a type with no handler: ```d return exp[0].tryMatch!( (IdentifierExpression exp) => exp.ident ); ```
 [...]
If "abort unless this `SumType` contains a particular type" is a common pattern in your code I would suggest extracting it into a helper function: ```d void require(T, S)(S s) if (isSumType!S) { s.match!( (T t) {} (_) { assert(0); } ); } ``` But in general, I think this is the wrong approach. What I would do instead is create a helper function like the following: ```d Optional!T maybeGet(T, S)(S s) if (isSumType!S) { return s.match!( (T t) => some(t), _ => no!T; ); } ``` This narrows the set of cases you have to deal with from "anything that could be in the `SumType`" to "either `T` or not `T`", and from there you can handle the "not `T`" case however you want--abort, throw an exception, etc. The general principle here is ["Parse, don't validate."][1] [1]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
 [...]
There is actually a workaround for this: ```d return exp[0].match!( (IdentifierExpression exp) => exp.ident, function string (_) { throw new Exception("..."); } ); ``` Note the explicit return type of `string` on the second lambda. The compiler will allow this, even though the lambda never returns a `string`, because it never returns normally at all.
I have used something like the following (although I am finding I use this type of thing less the longer I use sumtype): import std; struct A {} struct B {} alias Sum = SumType!(A,B); T asserter(T)(string errMsg,string file =__FILE__,ulong line=__LINE__) { assert(0,format("%s(%s): %s",file,line,errMsg)); } T as(T,S)(S s, string file=__FILE__,ulong line=__LINE__) if(isSumType!S) { return s.match!((T t)=>t,_=>asserter!T("Sum contains unexpected type",file,line)); } void main() { Sum s = Sum(A.init); auto a = s.as!A; writeln(a); auto b = s.as!B; }
Aug 29
prev sibling next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 18 March 2021 at 14:42:03 UTC, Oleg B wrote:
 Hello everyone.

 Went to see what is being prepared in the new compiler and saw 
 this
 https://dlang.org/changelog/pending.html#std-sumtype

 [snip]
Regardless of the other comments, it looks like the next release will be a big one.
Mar 18
prev sibling next sibling parent reply ryuukk_ <ryuukk_ gmail.com> writes:
 More general question: why phobos so bad structured? No way to 
 determine what can be used without gc, that need it (other D 
 features like betterC in the same situation). Some basic 
 actions requires couple of std libs (std.file, std.path etc). 
 Why something things placed in experimental will be in 
 experimental during several years, other placed to std directly 
 (sumtype)?

 May be it's time to restructure std, and determine algorithm 
 for place new code to it (like DIPs)?
I agree 1000% things are confusing when you want to manage memory yourself, you either have to check every functions and see if they allocate or no, or force yourself to add nogc everywhere OR use betterC and have access to no std at all I personally picked the betterC option and use D like i'd use C/C++, and it feels much better, i have my own Allocator struct, and whenever i need to allocate memory, i ask for an allocator If only the std was designed like this, whenever you want to allocate, you ask user to provide an allocator Want use GC? use gc allocator, everyone wins
Mar 18
parent ryuukk_ <ryuukk_ gmail.com> writes:
Things would improve drastically if std wasn't designed with 
assumptions on how user will use something

STD should be here to serve as a base for people to design 
libraries, not to enforce how libraries should be designed, 
because it'll only serve one way to write software


but it's not good if it's something like C++, now everyone 
complain and rewrite their own STL, for the better, i feel the 
same with D's STD (the language itself is perfect)
Mar 18
prev sibling parent reply JN <666total wp.pl> writes:
On Thursday, 18 March 2021 at 14:42:03 UTC, Oleg B wrote:
 Hello everyone.

 Went to see what is being prepared in the new compiler and saw 
 this
 https://dlang.org/changelog/pending.html#std-sumtype
Does anyone know if it would be possible to make it support (it doesn't support it now) this kind of syntax: Temperature t1 = Fahrenheit(98.6); isFahrenheit(t1); // works isFahrenheit(Fahrenheit(98.6)); // doesn't work :(
Mar 22
next sibling parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Monday, 22 March 2021 at 19:24:54 UTC, JN wrote:
 On Thursday, 18 March 2021 at 14:42:03 UTC, Oleg B wrote:
 Hello everyone.

 Went to see what is being prepared in the new compiler and saw 
 this
 https://dlang.org/changelog/pending.html#std-sumtype
Does anyone know if it would be possible to make it support (it doesn't support it now) this kind of syntax: Temperature t1 = Fahrenheit(98.6); isFahrenheit(t1); // works isFahrenheit(Fahrenheit(98.6)); // doesn't work :(
Not that it really makes sense, but if you wanted to do that you could cast: isFahrenheit(cast(Temperature)Fahrenheit(98.6)) //works Otherwize you would have to rewrite isFahrenheit
Mar 22
parent JN <666total wp.pl> writes:
On Monday, 22 March 2021 at 20:43:12 UTC, Imperatorn wrote:
 On Monday, 22 March 2021 at 19:24:54 UTC, JN wrote:
 On Thursday, 18 March 2021 at 14:42:03 UTC, Oleg B wrote:
 Hello everyone.

 Went to see what is being prepared in the new compiler and 
 saw this
 https://dlang.org/changelog/pending.html#std-sumtype
Does anyone know if it would be possible to make it support (it doesn't support it now) this kind of syntax: Temperature t1 = Fahrenheit(98.6); isFahrenheit(t1); // works isFahrenheit(Fahrenheit(98.6)); // doesn't work :(
Not that it really makes sense, but if you wanted to do that you could cast: isFahrenheit(cast(Temperature)Fahrenheit(98.6)) //works Otherwize you would have to rewrite isFahrenheit
isFahrenheit(Temperature(Fahrenheit(98.6))) works too. but that's still extra typing.
Mar 22
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 22 March 2021 at 19:24:54 UTC, JN wrote:
 Does anyone know if it would be possible to make it support (it 
 doesn't support it now) this kind of syntax:

 Temperature t1 = Fahrenheit(98.6);
 isFahrenheit(t1); // works
 isFahrenheit(Fahrenheit(98.6)); // doesn't work :(
You can do this by adding an additional overload to isFahrenheit: bool isFahrenheit(T)(T t) if (staticIndexOf!(T, Temperature.Types) >= 0) { return is(T == Fahrenheit); } bool isFahrenheit(Temperature temp) { // Forward to template overload return temp.match!isFahrenheit; }
Mar 22
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 22 March 2021 at 21:43:58 UTC, Paul Backus wrote:
 On Monday, 22 March 2021 at 19:24:54 UTC, JN wrote:
 Does anyone know if it would be possible to make it support 
 (it doesn't support it now) this kind of syntax:

 Temperature t1 = Fahrenheit(98.6);
 isFahrenheit(t1); // works
 isFahrenheit(Fahrenheit(98.6)); // doesn't work :(
You can do this by adding an additional overload to isFahrenheit: bool isFahrenheit(T)(T t) if (staticIndexOf!(T, Temperature.Types) >= 0) { return is(T == Fahrenheit); } bool isFahrenheit(Temperature temp) { // Forward to template overload return temp.match!isFahrenheit; }
What about something like Fahrenheit t2 = t1.to!(BaseTypeOf!(typeof(t1))); that could then be put into its own function.
Mar 22
parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 22 March 2021 at 22:03:07 UTC, jmh530 wrote:
 On Monday, 22 March 2021 at 21:43:58 UTC, Paul Backus wrote:
 On Monday, 22 March 2021 at 19:24:54 UTC, JN wrote:
 Does anyone know if it would be possible to make it support 
 (it doesn't support it now) this kind of syntax:

 Temperature t1 = Fahrenheit(98.6);
 isFahrenheit(t1); // works
 isFahrenheit(Fahrenheit(98.6)); // doesn't work :(
You can do this by adding an additional overload to isFahrenheit: bool isFahrenheit(T)(T t) if (staticIndexOf!(T, Temperature.Types) >= 0) { return is(T == Fahrenheit); } bool isFahrenheit(Temperature temp) { // Forward to template overload return temp.match!isFahrenheit; }
What about something like Fahrenheit t2 = t1.to!(BaseTypeOf!(typeof(t1))); that could then be put into its own function.
Not sure what `BaseTypeOf` is supposed to do here, but if you want to extract a particular type from a SumType it's pretty easy: Target to(Target, Source)(Source source) if (isSumType!Source && staticIndexOf!(Target, Source.Types)
= 0)
{ // Will throw MatchException if the wrong type is found return source.tryMatch!((Target target) => target)); }
Mar 22
parent jmh530 <john.michael.hall gmail.com> writes:
On Monday, 22 March 2021 at 23:25:07 UTC, Paul Backus wrote:
 [snip]

 Not sure what `BaseTypeOf` is supposed to do here, but if you 
 want to extract a particular type from a SumType it's pretty 
 easy:

 Target to(Target, Source)(Source source)
     if (isSumType!Source && staticIndexOf!(Target, 
 Source.Types) >= 0)
 {
     // Will throw MatchException if the wrong type is found
     return source.tryMatch!((Target target) => target));
 }
I was BaseTypeofOf!Temperature would be returning Fahrenheit in that case. But yes, this looks like it would do what I was thinking.
Mar 22
prev sibling parent Meta <jared771 gmail.com> writes:
On Monday, 22 March 2021 at 19:24:54 UTC, JN wrote:
 On Thursday, 18 March 2021 at 14:42:03 UTC, Oleg B wrote:
 Hello everyone.

 Went to see what is being prepared in the new compiler and saw 
 this
 https://dlang.org/changelog/pending.html#std-sumtype
Does anyone know if it would be possible to make it support (it doesn't support it now) this kind of syntax: Temperature t1 = Fahrenheit(98.6); isFahrenheit(t1); // works isFahrenheit(Fahrenheit(98.6)); // doesn't work :(
I would love this too, but Walter is very against implicit conversions. IMO this is one of the major reasons that algebraic types as a library are not adequate compared to a built-in language feature in D.
Mar 22