问题
Now Solved
I want to build an enum with a macro, including defining its type parameters.
There are a couple of sources describing adding enum fields with macros , but I havent found any which describe how to build an enum with specified parameter types using macros. There is a documentation entry made for limitations of macros here regarding parameter types, but that is still left empty.
The idea is to use a macro to generate a specified number of Either enums with increasing amount of parameter types.
//Either.hx
@:build(macros.build.EitherBuildMacro.build(10))
// enum Either {} <- this isnt sufficient as we need to generated several
// enums (in this example 10 of them) with parameter types...
//And it should generate
enum Either2<A,B>{
_1(value:A);
_2(value:B);
}
enum Either3<A,B,C>{
_1(value:A);
_2(value:B);
_3(value:C);
}
enum Either4<A,B,C,D>{
_1(value:A);
_2(value:B);
_3(value:C);
_4(value:D);
}
//etc until enum Either10<A,B,C,D,E,F,G,H,I,J>
As I showed earlier in this post there is an article describing how to add fields, but not types. I am clueless how to set these parameter types by a macro and it seems like there are some limitations, yet undocumented. Any pointers which command to use for that are highly appreciated. Defining series of Enums with increasing parameterization is typically something you rather want to do with build macros, than to do by hand. Especially since you could pare each macro generated EitherN with a macro generated OneOfN abstract
abstract OneOf2<A, B>(Either<A, B>) from Either<A, B> to Either<A, B> {
@:from inline static function fromA<A, B>(value:A):OneOf<A, B> {
return _1(a);
}
@:from inline static function fromB<A, B>(value:B):OneOf<A, B> {
return _2(b);
}
@:to inline function toA():Null<A> return switch(this) {
case _1(value): value;
default: null;
}
@:to inline function toB():Null<B> return switch(this) {
case _2(value): value;
default: null;
}
}
abstract OneOf3<A, B, C>(Either<A, B, C>) from Either<A, B, C> to Either<A, B, C> {
@:from inline static function fromA<A, B, C>(value:A):OneOf<A, B, C> {
return _1(value);
}
@:from inline static function fromB<A, B, C>(value:B):OneOf<A, B, C> {
return _2(value);
}
@:from inline static function fromC<A, B, C>(value:C):OneOf<A, B, C> {
return _3(value);
}
@:to inline function toA():Null<A> return switch(this) {
case _1(value): value;
default: null;
}
@:to inline function toB():Null<B> return switch(this) {
case _2(value): value;
default: null;
}
@:to inline function toC():Null<C> return switch(this) {
case _3(value): value;
default: null;
}
}
//etc
The same idea would be handy to generate series of Tuples and Functions with increasing amount of parameter types. Would be a efficient and flexible way to generate the right amount of enums, abstracts and typedefs
回答1:
@:build()
indeed isn't the right approach here, since that just builds one particular type. Instead, you could use an initialization macro in combination with Context.defineType():
--macro Macro.init()
import haxe.macro.Context;
class Macro {
public static function init() {
for (i in 2...11) {
Context.defineType({
pack: [],
name: "Either" + i,
pos: Context.currentPos(),
kind: TDEnum,
fields: [
for (j in 0...i) {
name: "_" + (j + 1),
kind: FFun({
args: [
{
name: "value",
type: TPath({
name: String.fromCharCode(65 + j),
pack: []
})
}
],
ret: null,
expr: null
}),
pos: Context.currentPos()
}
],
params: [
for (j in 0...i) {
name: String.fromCharCode(65 + j)
}
]
});
}
}
}
With -D dump=pretty
you can see that this generates Either2
-10
:
With for instance Either2.dump
looking like this:
@:used
enum Either2<A : Either2.A,B : Either2.B> {
_1(value:Either2.A);
_2(value:Either2.B);
}
Alternatively, you could consider using @:genericBuild()
in combination with a Rest
type parameter. That would essentially do the same and still use Context.defineType()
, with a few advantges:
- it would allow you to avoid encoding the number of type parameters into the type name (so it would just be
Either
instead ofEither2
/3
/ etc) - the amount of type parameters would not be limited to an arbitrary amount such as 10
- types would only be generated "on demand"
You can find an example here.
来源:https://stackoverflow.com/questions/57452808/how-to-make-an-parametrized-enum-build-macro