#Generating enums with functions

1 messages · Page 1 of 1 (latest)

wispy roost
#

I am trying to create enums that have an additional "negative" value for each value in the original enum.
I can generate said enums by creating additional fields with suffixed names.
I would also like to have a "negation" operation that switches between suffixed and non-suffixed members.

Not that important, but I would prefer if there was no way to create negative fields without calling "negate" on their counterparts.

    const fields = @typeInfo(T).Enum.fields;
    const multiplier = 2;
    var enumFields: [fields.len * multiplier]std.builtin.Type.EnumField = undefined;
    var decls = [_]std.builtin.Type.Declaration{};
    inline for (fields, 0..) |field, i| {
        const base = i * multiplier;
        enumFields[base + 0] = .{
            .name = field.name,
            .value = base + 0,
        };
        enumFields[base + 1] = .{
            .name = field.name ++ "_",
            .value = base + 1,
        };
    }
    return @Type(.{
        .Enum = .{
            .tag_type = std.math.IntFittingRange(0, fields.len * multiplier - 1),
            .fields = &enumFields,
            .decls = &decls,
            .is_exhaustive = true,
        },
    });
}

Problem is, I don't know how I would declare a functions for generated enums.
The code above only works if I leave decls empty.
Otherwise, it is failing with error message "reified enums must have no decls."

This example is more how I would like it to be:

pub const baz = WithNegation(foo);

pub const biz = enum {
    bar,
    bar_,

    pub fn negate(self: biz) biz {
        switch (self) {
            .bar => return .bar_,
            .bar_ => return .bar,
        }
    }
};```

Is there a way to do what I want?
mighty osprey
#

It is as the error says, there isn't a way to reify decls

#

You could create a free function that does it

mighty osprey
#
pub fn negate(comptime E: type, val: WithNegation(E)) WithNegation(E) {
    const int = @enumToInt(val);
    if (int % 2 == 0) {
        return @intToEnum(WithNegation(E), int + 1);
    } else return @intToEnum(WithNegation(E), int - 1);
}
#

Then you just end up doing negate(T, .foo)

wary cedar
wispy roost
mighty osprey
#

You could just take anytype and assume it's the correct type

#

Need to get home

#

If i think of something I'll let you know

mighty osprey
#

Yeah, if you don't want to supply the type I'd just use anytype

#

That of course comes with the drawback that you could call it with any type, including values that aren't created by your function

#

Another alternative that I probably wouldn't do myself, but which is possible, is to return a namespace, like

pub fn WithNegation(comptime E: type) type {
    return struct {
        pub const Negated = blk: {
            // -- snip --
        };
        pub fn negate(val: Negated) Negated {
            // -- snip --
        }
     };
}
#

And then you'd get both the type and the function in the single namespace

mighty osprey
#

It's similar to enum literals

#

Or how @import exclusively takes a string literal, not an expression

wispy roost
mighty osprey
#

Well, might I ask what you intend to use this negatable enum stuff for?

compact moth
#

Right - highly-meta stuff is often done in places where something simpler, or even one-off, would do.

wispy roost
mighty osprey
#

right, but I'm moreso asking what you're using this in particular for

compact moth
# wispy roost I am working on some generalized geometric algorithms and I needed some types fo...

There is a common problem in software engineering where people try to write the most maximally generic code they they possibly could, because of the idea that it should be reusable, and that they want others to be able to use it, or make sense of it.

However, this is---so goes the observation---a falsehood.
The observation is that---actually---keeping the code as simple as possible, and as concrete as possible - is actually better in all those cases; more reusable, and more grokkable.

Indeed, for APIs too; One way to do it well may be to build a set of low level parts, then build a couple of simple 80%-of-people-want-this APIs over it; this way, you get what you need, and others that need more can reach in if they need to -- one reason I dislike public/private, incidentally.