Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about mir.algebraic.visit #34

Closed
deviator opened this issue Dec 18, 2020 · 3 comments
Closed

Question about mir.algebraic.visit #34

deviator opened this issue Dec 18, 2020 · 3 comments
Assignees
Labels
enhancement New feature or request

Comments

@deviator
Copy link

Hello! I have this code:

/+ dub.sdl:
    name "app"
    dependency "mir-core" version="1.1.54"
+/

version = short_visit;

import mir.algebraic;
import std.stdio;

enum Bool : bool { false_ = false, true_ = true }

alias Value = TaggedVariant!(
    ["str", "raw", "bit", "i8",  "u8",  "i16", "u16", "i32", "u32", "i64", "u64", "f32", "f64"],
    string, const(void)[], Bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double,
);

void foo(in Value val)
{
    version (visit)
    {
        val.visit!(
            (string v) { writeln("str: ", v); },
            (const(void)[] v) { writeln("raw: ", v); },
            (Bool v) { writeln("bit: ", cast(bool)v ? "true" : "false"); },
            (float v) { writefln("f32: %.8f", v); },
            (double v) { writefln("f64: %.15f", v); },
            (v) { writeln("int: ", v); }, // int's
        );
    }
    else version(full_visit)
    {
        val.visit!(
            (string v) { writeln("str: ", v); },
            (const(void)[] v) { writeln("raw: ", v); },
            (Bool v) { writeln("bit: ", cast(bool)v ? "true" : "false"); },
            (float v) { writefln("f32: %.8f", v); },
            (double v) { writefln("f64: %.15f", v); },
            (byte v) { writeln("int: ", v); },
            (ubyte v) { writeln("int: ", v); },
            (short v) { writeln("int: ", v); },
            (ushort v) { writeln("int: ", v); },
            (int v) { writeln("int: ", v); },
            (uint v) { writeln("int: ", v); },
            (long v) { writeln("int: ", v); },
            (ulong v) { writeln("int: ", v); },
        );
    }
    else version (short_visit)
    {
        val.visit!(v => writefln("%s: %s", val.kind, v));
    }
    else
    {
        template TypeByKind(T, alias kind) if (isTaggedVariant!T && is(typeof(kind) == T.Kind))
        {
            import std : staticIndexOf, EnumMembers;
            alias TypeByKind = T.AllowedTypes[staticIndexOf!(kind, EnumMembers!(T.Kind))];
        }

        FS: final switch (val.kind) with (Value.Kind)
        {
            case str: writeln("str: ", val.get!string); break;
            case raw: writeln("raw: ", val.get!(const(void)[])); break;
            case bit: writeln("bit: ", cast(bool)(val.get!Bool) ? "true" : "false"); break;
            case f32: writefln("f32: %.8f", val.get!float); break;
            case f64: writefln("f64: %.15f", val.get!double); break;

            static foreach (i, k; [i8, u8, i16, u16, i32, u32, i64, u64])
            {
            case k: writeln("int: ", val.get!(TypeByKind!(Value,k))); break FS;
            }
        }
    }
}

void main() { foo(Value(10)); }

With version = short_visit; or version = full_visit; all works.
With version = visit; it get compile time error:

path/to/mir/algebraic.d(2515,33): Error: static assert:  "const(Algebraic!(...types...)): the visitor cann't be caled with arguments (byte)"

but it have last "default" function without type specification for argument what can be instanced for all int types.

This is a bug or it by design?

Original code is more complex and I found useful variant with switch by now (the desire to have get by kind from here).

@9il 9il self-assigned this Dec 18, 2020
@9il
Copy link
Member

9il commented Dec 18, 2020

Thanks for the report, I will take a look. As for now, small simplification alias TypeByKind = T.AllowedTypes[kind]; should work.

@9il
Copy link
Member

9il commented Dec 18, 2020

Yes, this is by design.
Mir uses common D overload resolution, unlike other similar packages.
So both float and double visitors have priority for byte over a generic version like it would be between overloaded functions.

I am a bit conservative in case of adding special overloading resolution similar to other libs, this needs a lot of things to think about like classes/interface inheritance, alias this, and implicit conversions.

Instead, it seems to better add another one set of handles that allow splitting algebraic type into sub-types like:

alias IntegralValue = Variant!(byte, ubyte, short, ushort, int, uint, long, long);

...
v.splitVisit!(
            (string v) { writeln("str: ", v); },
            (const(void)[] v) { writeln("raw: ", v); },
            (Bool v) { writeln("bit: ", cast(bool)v ? "true" : "false"); },
            (float v) { writefln("f32: %.8f", v); },
            (double v) { writefln("f64: %.15f", v); },
            (IntegralValue v) { v.visit!( v => writeln("int: ", v)); }, // int's
);

Two workarounds for now:

/+ dub.sdl:
    name "app"
    dependency "mir-core" version="1.1.54"
+/

version = short_visit;

import mir.algebraic;
import std.stdio;

enum Bool : bool { false_ = false, true_ = true }

alias Value = TaggedVariant!(
    ["str", "raw", "bit", "i8",  "u8",  "i16", "u16", "i32", "u32", "i64", "u64", "f32", "f64"],
    string, const(void)[], Bool, byte, ubyte, short, ushort, int, uint, long, ulong, float, double,
);

version = visit_with_static;

void foo(in Value val)
{
    import std.traits;
    version (visit_with_static)
    {
        static void f32Handle(T)(const T v) if (is(T == float)) { writefln("f32: %.8f", v); };
        static void f64Handle(T)(const T v) if (is(T == double)) { writefln("f64: %.15f", v); };
        static void intHandle(T)(const T v) if (isIntegral!T) { writeln("int: ", v); }; // int's

        val.visit!(
            (string v) { writeln("str: ", v); },
            (const(void)[] v) { writeln("raw: ", v); },
            (Bool v) { writeln("bit: ", cast(bool)v ? "true" : "false"); },
            f32Handle,
            f64Handle,
            intHandle,
        );
    }
    else
    {
        val.visit!(
            (string v) { writeln("str: ", v); },
            (const(void)[] v) { writeln("raw: ", v); },
            (Bool v) { writeln("bit: ", cast(bool)v ? "true" : "false"); },
            (v) {
                alias T = Unqual!(typeof(v));
                static if (is(T == float))
                    writefln("f32: %.8f", v);
                else
                static if (is(T == double))
                    writefln("f64: %.8f", v);
                else
                    writeln("int: ", v);
            }
        );
    }
}

unittest { foo(Value(10)); }

@9il 9il added the enhancement New feature or request label Dec 18, 2020
@9il
Copy link
Member

9il commented Dec 18, 2020

@deviator Kind based get/trustedGet has been added.
http://mir-core.libmir.org/mir_algebraic.html#TaggedVariant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants