KevinResoL
KevinResoL

Reputation: 982

Unify generic types in macro

I wanted to use macro to check if a function is returning a particular generic type, say Array, so it is fine if the function is returning Array<Dynamic>, Array<String>, or even generic Array<T>.

So I tried to Context.unify it with Array<Dynamic>. It is fine for Array<String> or Array<Dynamic> but it fails when the type parameter is "generic" because the ComplexType Array<T> won't convert to a Type with Type not found: T (See code below). Are there any possible ways to achieve what I am attempting to do?

package;

#if macro
import haxe.macro.Context;
using haxe.macro.ComplexTypeTools;
#end

#if !macro @:build(Macros.build()) #end
class Main 
{
    public function test<T>():Array<T>
    {
        return [];
    }
}

class Macros
{
    public static function build()
    {
        #if macro
        var fields = Context.getBuildFields();
        for(field in fields)
        {
            switch(field.kind)
            {
                case FFun(f):
                    // try to unify Array<String> with Array<Dynamic>
                    trace(Context.unify((macro:Array<String>).toType(), (macro:Array<Dynamic>).toType())); 
                    // true

                    // try to unify Array<T> with Array<Dynamic>
                    trace(Context.unify(f.ret.toType(), (macro:Array<Dynamic>).toType())); 
                    // Type not found: T
                default:
            }
        }
        return null;
        #end
    }
}

Upvotes: 1

Views: 374

Answers (1)

Mihail Ignatiev
Mihail Ignatiev

Reputation: 853

UPDATE

So, checking TPath was not the best idea. Based on the previous assumption about Dynamic being assignable to any type we can replace unconvertable type parameter with the Dynamic (eg Array<T> = Array<Dynamic>) and when try to unify it.

static function toTypeExt(c:ComplexType):Null<haxe.macro.Type>
{
    try {
        return c.toType();          
    } catch (error:Error)
    {                   
        switch (c)                          
        {
            case TPath(p):                  
                //deep copy of the TPath with all type parameters as Dynamic
                var paramsCopy = [for (i in 0...p.params.length) TPType(macro:Dynamic)];
                var pCopy:TypePath = {
                    name: p.name,
                    pack: p.pack,
                    sub: p.sub,
                    params: paramsCopy
                }
                var cCopy = TPath(pCopy);

                //convert after
                return cCopy.toType();
            default:                    
        }
    }

    return null;
}

Use toTypeExt() in your build macro instead of toType.

trace(Context.unify(toTypeExt(f.ret), (macro:Array<Dynamic>).toType()));

Looks more like a workaround to me, but there is a strange thing about ComplexTypeTools.toType - it will succeed with a class type parameter while failing with method type parameter.

OLD ANSWER

Unification won't work since there is no way of converting ComplexType with the type parameter to Type (in that context). But since you are unifying with Array it is safe to assume that any Array will unify with it (since any type is assignable to Dynamic http://haxe.org/manual/types-dynamic.html).


May be it is not the pritiest solution, but simple TPath check is the way to go here:

    case FFun(f):                   
        switch (f.ret) {
                case TPath({ name: "Array", pack: [], params: _ }):
                    trace("Yay!");
                default:                            
        }

Upvotes: 2

Related Questions