std.sumtype?

Steven Schveighoffer schveiguy at gmail.com
Mon Mar 29 15:41:39 UTC 2021


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


More information about the Digitalmars-d mailing list