'What is the best way to get Haxe function parameter types using a macro?

I want to get the parameter types of a Haxe function using a macro and convert them to a shorthand string form, a bit like JNI/Java method signatures, but without a return type.

The motivation here is to provide access to the function parameter types, without having to slowly search through run-time type information at runtime. For example, say you want to construct a graphical widget for calling a function that takes parameters. You will need the type of each function parameter to create the correct spinbox, textbox, and select box widgets needed for tweaking the values that will be passed to the function.

So the question is, how can you save Haxe function parameter types with a macro?



Solution 1:[1]

A more up to date approach..

macro function deflate(fun:haxe.macro.Expr) {
    var type = haxe.macro.Context.typeof(fun);
    final paramNames = extractFunction(type);

    return macro $v{paramNames};
}
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
    return switch type {
        case TFun(args, ret): {
                var paramNames:Array<Dynamic> = [];
                for (p in args) {
                    final pName = p.name;
                    paramNames.push(pName);
                }

                return paramNames;
            }
        case _: {throw "unable to extract function information";};
    }
}

Use it like this

using Macros;
function func(name:String, greeting:String){};
final args = fun.deflate(); 
trace(args) // output: [name, greeting]

A problem you may face is how to collect the default value of a parameter, consider the example below.

function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = fun.deflate(); 
trace(args) // output: [name, greeting]

Now let's account for default parameter values by slightly modifying the code:

// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
    return switch type {
        case TFun(args, ret): {
                var paramNames:Array<Dynamic> = [];
                for (p in args) {
                    final pName = p.name;
                    final v = {name: pName, value: null}; // <= anticipate a value
                    paramNames.push(v);
                }

                return paramNames;
            }
        case _: {throw "unable to extract function information";};
    }
}


macro function deflate(fun:haxe.macro.Expr) {
    var type = haxe.macro.Context.typeof(fun);
    final paramNames:Array<Dynamic> = extractFunction(type);
    // extract default param values 
    switch  fun.expr {
        case EFunction(f, m):{
            for(a in m.args){
                for(p in paramNames){
                    if(p.name == a.name){
                        if(a.value != null){
                            switch (a.value.expr){
                                case EConst(c):{
                                    switch(c){
                                        case CString(v, _):{
                                            p.value = v;
                                        }
                                        case CFloat(f): {
                                            p.value = Std.parseFloat(f);
                                        }
                                        case CInt(i):{
                                            p.value = Std.parseInt(i);
                                        }
                                        case _: throw "unsupported constant value for default parameter";
                                    }
                                }
                                case _:
                            }
                        }
                    }
                }
            }
        }
        case _:
    }
    return macro $v{paramNames};
}

So we can now use it like this

function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = Macros.deflate(func); 
trace(args) // output: [{name: 'name', value:'Josh', {name:'greeting', value:'Hello'}]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1