【问题标题】:How to make an parametrized enum build macro?如何制作参数化的枚举构建宏?
【发布时间】:2019-08-11 19:06:59
【问题描述】:

Now Solved

我想用宏构建一个枚举,包括定义它的类型参数。

有几个来源描述了使用 macros 添加枚举字段,但我还没有找到任何描述如何使用宏构建具有指定参数类型的枚举。有一个文档条目是为了限制宏 here 关于参数类型,但它仍然是空的。

这个想法是使用一个宏来生成指定数量的 Either 枚举,并增加参数类型的数量。

//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>

正如我在本文前面所展示的,有一篇文章描述了如何添加字段,而不是类型。我不知道如何通过宏设置这些参数类型,似乎有一些限制,但没有记录。任何用于该命令的指针都受到高度赞赏。通过增加参数化定义一系列枚举通常是您更愿意使用构建宏来完成的事情,而不是手动完成。特别是因为您可以将每个宏生成的 EitherN 与生成的宏 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

同样的想法可以方便地生成一系列具有越来越多参数类型的元组和函数。将是一种有效且灵活的方式来生成适量的枚举、摘要和类型定义

【问题讨论】:

标签: macros haxe


【解决方案1】:

@:build() 在这里确实不是正确的方法,因为这只是构建一种特定类型。相反,您可以将initialization macroContext.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)
                    }
                ]
            });
        }
    }
}

使用-D dump=pretty,您可以看到这会生成Either2-10

例如 Either2.dump 看起来像这样:

@:used
enum Either2<A : Either2.A,B : Either2.B> {
    _1(value:Either2.A);
    _2(value:Either2.B);
}

或者,您可以考虑将@:genericBuild()Rest 类型参数结合使用。这基本上会做同样的事情,并且仍然使用Context.defineType(),但有一些优点:

  • 它将允许您避免将类型参数的数量编码到类型名称中(因此它只是 Either 而不是 Either2 / 3 / 等)
  • 类型参数的数量不会限制为任意数量,例如 10
  • 类型只能“按需”生成

你可以找到一个例子here

【讨论】:

  • 非常感谢,您将我指向初始化宏和出色的解释,甚至努力提供一些代码。非常感谢!
  • 带有 Rest 的 @:genericBuild() 是一个非常好的解决方案!上帝我爱haxe :-)
猜你喜欢
  • 1970-01-01
  • 2015-12-03
  • 1970-01-01
  • 1970-01-01
  • 2013-03-29
  • 1970-01-01
  • 2019-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多