Abstract(Dynamic) as a Map key

I can’t understand why this code does not compile?

@:notNull
abstract Key<T>(Dynamic) from Class<T> from Enum<T> to Dynamic {
    inline public function new(value) {
        this = value;
    }
}

class Main {
	static function main() {
		var map:Map<Key<Dynamic>, String> = []; //error
	}
}

I can use the abstract as a key for haxe.ds.ObjectMap, but when I use haxe.ds.Map, I get an error:
Abstract haxe.ds.Map has no @:to function that accepts haxe.IMap<Key<Dynamic>, String>.

Why, in this case, the compiler cannot determine the actual type of map and how can I fix it?

What is your intent here? A map that can only hold keys of type Class<T> or Enum<T>? How about making an abstract that wraps haxe.ds.ObjectMap directly?

Yes, I have a method like

public function addComponent<T>(entity:Entity, key:Key<T>, value:T) {}

Components are stored on the map. And, of course, I can use ObjectMap or making an abstract (to use the array access syntax). In fact, this is not critical at all, I just wanted to understand for myself why I cannot use an existing tool instead of writing another with the same functions.

Because map have no implementation using Dynamic type as a key. Dynamic type is unique: since it can hold any type, it doesn’t unify with any type. You need to be more specific: using object type ({}) for example

:notNull
abstract Key<T>({}) to {}
    inline public function new(value) {
        this = value;
    }
    @:from public static function fromClass<T>(cl:Class<T>):Key<T> return cast cl;
    @:from public static function fromEnum<T>(en:EnumValue):Key<T> return cast en;
}
1 Like

Note that Class<T> or Enum<T> may or may not be {} depending on your target runtime.

For which targets will this cause problems? I seem to have tried this for many targets and it works fine.

I just wanted to point out that you are using the language in an unspecified way. It may or may not work. Or worse still it may work today but break another day. Because after all it is unspecified.

Is there any better way to have only Class<T> or Enum<T> as a valid value?

If it is viable for your use case, you may use a string map together with Type.getClassName and Type.getEnumName