romamik
romamik

Reputation: 595

Explain @:remove @:generic metadata combination

Consider class with type parameter, that should be used as Map key.

class Foo<K> {
  var map: Map<K, Dynamic> = new Map();
}

This does not compile with error Type parameters of multi type abstracts must be known.

The reason is understandable - Map is abstract and it's underlying type is selected based on key type, so key type should be known when compiling new Map() expression. On the other hand non-generic type with type parameters are compiled once for all parameters.

Looks like adding @:generic metadata should help. But actually it does not. My guess was that this is because haxe compiler compiles @:generic types the same as it does with non-generic types, and only then does some extra work for generic type. So I was thinking that having a Map with key type defined by type parameter is impossible in haxe.

But recently I've stumbled upon this issue: https://github.com/HaxeFoundation/haxe/issues/2537 Simn's answer there says, that this can be done by adding @:remove @:generic metadata. And it actually works.

@:remove @:generic
class Foo<K> {
  var map: Map<K, Dynamic> = new Map();
}

For me this looks like magic and I'm not comfortable with this. In documentation I only see that @:remove Causes an interface to be removed from all implementing classes before generation. That does not explain why this works.

Upvotes: 2

Views: 103

Answers (1)

Ilir Liburn
Ilir Liburn

Reputation: 222

If you replace Map by haxe.ds.BalancedTree and use only @:generic without @:remove, you will see your class Foo generated on your target containing new BalancedTree< object, object > (object is Dynamic). That means, Foo class holds general BalancedTree< object, object > type, while Foo_String for example holds generic BalancedTree< string, object > type. Now, if you go back to your Map, you will see that "Abstract haxe.ds.Map has no @:to function that accepts haxe.IMap< Foo.K, Dynamic >", e.g. no Map< Dynamic, Dynamic > implementation exists. That's why you need to use @:remove which will actually stop generation of Foo class, or at least, remove new Map< Dynamic, Dynamic > from the Foo (@:remove "Causes an interface to be removed" is misleading here)

Upvotes: 3

Related Questions