On Thursday, 7 September 2017 at 16:55:02 UTC, EntangledQuanta wrote:
Sorry, I think you missed the point completely... or I didn't explain things very well.

I don't think I did - your new explanation didn't change my understanding at least. This indicates I'm the one who's bad at explaining. Ah well.

The point of my post was mostly to rewrite the code you'd posted in a form that I (and, I hope, others) found easier to understand.

I see no where in your code where you have a variant like type.

True. I've now rewritten it to use std.variant.Algebraic with these semantics:

auto foo(T1, T2)(T1 a, T2 b, int n) {
    import std.conv;
return T1.stringof~": "~to!string(a)~" - "~T2.stringof~": "~to!string(b);
}

unittest {
    import std.variant;
    Algebraic!(float, int) a = 4f;
    Algebraic!(double, byte) b = 1.23;

    auto res = varCall!foo(a, b, 3);
    assert(res == "float: 4 - double: 1.23");
}

template varCall(alias fn) {
    import std.variant;
    auto varCall(int n = 0, Args...)(Args args) {
        static if (n == Args.length) {
            return fn(args);
        } else {
            auto arg = args[n];
            static if (is(typeof(arg) == VariantN!U, U...)) {
                foreach (T; arg.AllowedTypes) {
                    if (arg.type == typeid(T))
return varCall!(n+1)(args[0..n], arg.get!T, args[n+1..$]);
                }
                assert(false);
            } else {
                return varCall!(n+1)(args);
            }
        }
    }
}

Sadly, by using std.variant, I've given up on the elegant switch/case in exchange for a linear search by typeid. This can be fixed, but requires changes in std.variant.

Of course, it would be possible to hide all this behind compiler magic. Is that desirable? I frankly do not think so. We should be wary of adding too much magic to the compiler - it complicates the language and its implementation. This is little more than an optimization, and while a compiler solution would be less intrusive and perhaps more elegant, I do not feel it provides enough added value to warrant its inclusion.

Next, I'm curious about this code:

void bar(var t)
{
    writeln("\tbar: Type = ", t.type, ", Value = ", t);
}

void main()
{
   bar(3); // calls bar as if bar was `void bar(int)`
   bar(3.4f); // calls bar as if bar was `void bar(float)`
   bar("sad"); // calls bar as if bar was `void bar(string)`
}

What does 'var' add here, that regular templates do not? (serious question, I'm not trying to shoot down your idea, only to better understand it) One possible problem with var here (if I understand it correctly) would be separate compilation - a generated switch would need to know about types in other source files that may not be available at the time it is compiled.

Next:

var foo(var x)
{
   if (x == 3)
       return x;
   return "error!";
}

This looks like a sort of reverse alias this, which I've argued for on many occasions. Currently, it is impossible to implement a type var as in that function - the conversion from string to var would fail. A means of implementing this has been discussed since at least 2007, and I wrote a DIP[1] about it way back in 2013. It would make working with variants and many other types much more pleasant.

[1]: https://wiki.dlang.org/DIP52

Reply via email to