Thread overview
Is std.variant.visit not @nogc?
Apr 09, 2018
helxi
Apr 09, 2018
Adam D. Ruppe
Apr 09, 2018
Hasen Judy
Apr 09, 2018
Chris Katko
Apr 09, 2018
Paul Backus
Apr 10, 2018
helxi
Apr 10, 2018
Paul Backus
Apr 10, 2018
aliak
Apr 10, 2018
Paul Backus
April 09, 2018
import std.variant, core.stdc.stdio;

Algebraic!(T, string) fib_nth(T)(T n)
{
    return n % 15
                ? n % 5
                    ? n % 3
                        ? Algebraic!(T, string)(n)
                        : Algebraic!(T, string)("Fizz")
                    : Algebraic!(T, string)("Buzz")
                : Algebraic!(T, string)("Fizzbuzz");
}

void main() @nogc
{
    foreach (i; 1 .. 101)
    {
        fib_nth(i).visit!(
            (string s) => printf("%s\n", s.ptr),
            (int n) => printf("%i\n", n)
        );
    }
}


Complains source/app.d(18,19): Error: @nogc function D main cannot call non-@nogc function std.variant.visit!(function (string s) => printf("%s\x0a", cast(immutable(char)*)s), function (int n) => printf("%i\x0a", n)).visit!(VariantN!(16LU, int, string)).visit
/usr/bin/dmd failed with exit code 1.


If so, is there a way to emulate `visit` in a @nogc setting?
April 09, 2018
On Monday, 9 April 2018 at 03:20:58 UTC, helxi wrote:
> Is std.variant.visit not @nogc?

These error messages REALLY need to be fixed.

visit, being a template, is @nogc or not based on the arguments passed to it as well as its own body, so while the error messages point to visit itself, these are frequently actually caused the predicate your pass.

....well, in this case, it is actually visit itself.

phobos/std/variant.d(2464): Error: @nogc function std.variant.visitImpl!(true, VariantN!(8u, int, string), function (string s) => printf("%s\x0a", cast(immutable(char)*)s), function (int n) => printf("%i\x0a", n)).visitImpl cannot call non-@nogc constructor std.variant.VariantException.this
phobos/std/variant.d(2469): Error: @nogc function std.variant.visitImpl!(true, VariantN!(8u, int, string), function (string s) => printf("%s\x0a", cast(immutable(char)*)s), function (int n) => printf("%i\x0a", n)).visitImpl cannot call non-@nogc function std.variant.VariantN!(8u, int, string).VariantN.peek!int.peek
phobos/std/variant.d(2469): Error: @nogc function std.variant.visitImpl!(true, VariantN!(8u, int, string), function (string s) => printf("%s\x0a", cast(immutable(char)*)s), function (int n) => printf("%i\x0a", n)).visitImpl cannot call non-@nogc function std.variant.VariantN!(8u, int, string).VariantN.peek!string.peek
phobos/std/variant.d(2173): Error: template instance `std.variant.visitImpl!(true, VariantN!(8u, int, string), function (string s) => printf("%s\x0a", cast(immutable(char)*)s), function (int n) => printf("%i\x0a", n))` error instantiating


Ugh, so unreadable even on this level, but at least the actual information is there:

std.variant.VariantException.this is not marked @nogc (but it prolly could be)

VariantN.peek is not @nogc because it calls Object.opEquals... which is broken af, sadly, but can probably be fixed for this case by marking TypeInfo.opEquals nogc.


I think the peek one is going to be the harder one to work around since any reimplementation of peek is probably going to still call it... though MAYBE you can use `!is` instead of `!=`... and any reimplementation of visit needs to check types.


But if you wanna try to work around it, I would copy the visitImpl and visit functions out of std.variant and do some adjustments, then call your version instead (which will be fairly easy btw since they are already UFCS).



FYI: the way I got these error messages was to go into the Phobos source and add @nogc to the lowest level template in the instantiation chain. Then just recompile your program - no need to recompile Phobos itself since they are templates.

I wish the error messages would just do this for you (simulate @nogc at the second-highest level) to keep you from having to edit it yourself just to know what it is.

April 09, 2018
On Monday, 9 April 2018 at 03:41:17 UTC, Adam D. Ruppe wrote:
> On Monday, 9 April 2018 at 03:20:58 UTC, helxi wrote:
>
> visit, being a template, is @nogc or not based on the arguments passed to it as well as its own body, so while the error messages point to visit itself, these are frequently actually caused the predicate your pass.
>
> [....]
>
> std.variant.VariantException.this is not marked @nogc (but it prolly could be)
>
> VariantN.peek is not @nogc because it calls Object.opEquals... which is broken af, sadly, but can probably be fixed for this case by marking TypeInfo.opEquals nogc.
>


IMO, this is one more reason why sum-types should be built into the language compiler, instead of being implemented in user-space.
April 09, 2018
On Monday, 9 April 2018 at 07:02:50 UTC, Hasen Judy wrote:
> IMO, this is one more reason why sum-types should be built into the language compiler, instead of being implemented in user-space.

+1. Any time you have to "create" a fundamental feature in a language... from inside the language itself... you're going to have confusing error messages, and a huge uphill battle.

Look at Boost. While I salute the effort put into those libraries, trying to "fix C++"... "from inside C++" is a fools errand. The upgraded multidimensional array code looks like this:

 typedef boost::multi_array<double, 3> array_type;
  typedef array_type::index index;
  array_type A(boost::extents[3][4][2]);

as opposed to you know, in D:

 double [3][4][2] A;

And the D version has useful error messages, small compile times, and it's still easier to read, reason about, and debug.
April 09, 2018
On Monday, 9 April 2018 at 07:07:58 UTC, Chris Katko wrote:
> On Monday, 9 April 2018 at 07:02:50 UTC, Hasen Judy wrote:
>> IMO, this is one more reason why sum-types should be built into the language compiler, instead of being implemented in user-space.
>
> +1. Any time you have to "create" a fundamental feature in a language... from inside the language itself... you're going to have confusing error messages, and a huge uphill battle.

I agree in general, but in this case it's actually completely doable. In fact, I've done it myself: check out 'sumtype' on code.dlang.org. You can replace 'Algebraic' with 'SumType' and 'visit' with 'match' in helxi's example, and everything Just Works™:

import sumtype;

import core.stdc.stdio;

SumType!(T, string) fib_nth(T)(T n)
{
    return n % 15
                ? n % 5
                    ? n % 3
                        ? SumType!(T, string)(n)
                        : SumType!(T, string)("Fizz")
                    : SumType!(T, string)("Buzz")
                : SumType!(T, string)("Fizzbuzz");
}

void main() @nogc
{
    foreach (i; 1 .. 101)
    {
        fib_nth(i).match!(
            (string s) => printf("%s\n", s.ptr),
            (int n) => printf("%i\n", n)
        );
    }
}
April 10, 2018
On Monday, 9 April 2018 at 15:59:32 UTC, Paul Backus wrote:
> On Monday, 9 April 2018 at 07:07:58 UTC, Chris Katko wrote:
>> [...]
>
> I agree in general, but in this case it's actually completely doable. In fact, I've done it myself: check out 'sumtype' on code.dlang.org. You can replace 'Algebraic' with 'SumType' and 'visit' with 'match' in helxi's example, and everything Just Works™:
>
> [...]

This isn't boxed by any chance, is it?
April 10, 2018
On Tuesday, 10 April 2018 at 00:22:18 UTC, helxi wrote:
> On Monday, 9 April 2018 at 15:59:32 UTC, Paul Backus wrote:
>> On Monday, 9 April 2018 at 07:07:58 UTC, Chris Katko wrote:
>>> [...]
>>
>> I agree in general, but in this case it's actually completely doable. In fact, I've done it myself: check out 'sumtype' on code.dlang.org. You can replace 'Algebraic' with 'SumType' and 'visit' with 'match' in helxi's example, and everything Just Works™:
>>
>> [...]
>
> This isn't boxed by any chance, is it?

Nope! It's just a tagged union, almost exactly the same as what you'd write by hand in C. You can take a look at the source yourself, if you're curious---it's actually pretty simple:

https://github.com/pbackus/sumtype/blob/master/src/sumtype.d#L27
April 10, 2018
On Tuesday, 10 April 2018 at 03:48:25 UTC, Paul Backus wrote:
> Nope! It's just a tagged union, almost exactly the same as what you'd write by hand in C. You can take a look at the source yourself, if you're curious---it's actually pretty simple:
>
> https://github.com/pbackus/sumtype/blob/master/src/sumtype.d#L27

Awesome!

this is a neat trick:

union
{
  AliasSeq!(T0, T1) values;
}

Is that usage documented somewhere, or is it somewhere in phobos maybe?

Also, can Algebraic be fully replaced with this version then or is there some functionality that would stop it going through?
April 10, 2018
On Tuesday, 10 April 2018 at 12:34:07 UTC, aliak wrote:
> Awesome!
>
> this is a neat trick:
>
> union
> {
>   AliasSeq!(T0, T1) values;
> }
>
> Is that usage documented somewhere, or is it somewhere in phobos maybe?
>
> Also, can Algebraic be fully replaced with this version then or is there some functionality that would stop it going through?

It's called "type sequence instantiation", and it's documented in the "Compile-time Sequences" article [1] on dlang.org. I discovered it reading the source of the 'tagged_union' dub package [2]. It's not mentioned anywhere in the language spec, as far as I can tell.

SumType should be capable of doing anything that Algebraic can do (if it's not, please open an issue on Github!), but it's not a drop-in replacement, and it's still a work in progress. Documentation for the current version (more or less) is available at http://sumtype.dpldocs.info/index.html. If there are any particular features you'd like to see, let me know, and I'll do my best to add them.

[1] https://dlang.org/articles/ctarguments.html#type-seq-instantiation
[2] https://github.com/Superstar64/tagged_union/blob/master/source/tagged_union.d
April 10, 2018
On 4/10/18 12:59 PM, Paul Backus wrote:
> On Tuesday, 10 April 2018 at 12:34:07 UTC, aliak wrote:
>> Awesome!
>>
>> this is a neat trick:
>>
>> union
>> {
>>   AliasSeq!(T0, T1) values;
>> }
>>
>> Is that usage documented somewhere, or is it somewhere in phobos maybe?
>>
>> Also, can Algebraic be fully replaced with this version then or is there some functionality that would stop it going through?
> 
> It's called "type sequence instantiation", and it's documented in the "Compile-time Sequences" article [1] on dlang.org. I discovered it reading the source of the 'tagged_union' dub package [2]. It's not mentioned anywhere in the language spec, as far as I can tell.

Ooh! that's really cool.

I've put AliasSeq of types into a struct, but never considered putting it into a union. This has a lot of potential when you want to auto-generate unions.

Nice trick!

-Steve