如何使参数化的枚举生成宏? [英] How to make an parametrized enum build macro?

查看:121
本文介绍了如何使参数化的枚举生成宏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

现在已解决

我想用一个宏来构建一个枚举,包括定义其类型参数.

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.

这个想法是使用一个宏来生成指定数量的Either枚举,而这些枚举的参数类型会越来越多.

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>

正如我在本文前面所展示的,有一篇文章描述了如何添加字段,而不是类型.我不知道如何通过宏设置这些参数类型,并且似乎有一些限制,但是没有记录.非常感谢使用该命令的任何指针.通常,您更希望对构建宏进行定义,而不要手工完成,而要通过增加参数化来定义一系列枚举.特别是因为您可以将生成的每个宏与使用 OneOfN摘要

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

使用相同的想法来生成随参数类型数量增加而生成的元组和函数序列的方法.将是生成适当数量的枚举,摘要和typedef的有效且灵活的方式

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

推荐答案

@:build()在这里确实不是正确的方法,因为它只能构建一种特定的类型.相反,您可以将初始化宏

@: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)
                    }
                ]
            });
        }
    }
}

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

With -D dump=pretty you can see that this generates 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(),但有一些优点:


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:

  • 这将使您避免将类型参数的数量编码为类型名称(因此它将是Either而不是Either2/3/等)
  • 类型参数的数量将不限于任意数量,例如10
  • 类型只能按需"生成
  • it would allow you to avoid encoding the number of type parameters into the type name (so it would just be Either instead of Either2 / 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"

您可以在此处找到示例.

这篇关于如何使参数化的枚举生成宏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆