My problem is that haxe doesn't want to compile a following snippet to lua target. It complains about:
Main.hx:7: characters 11-17 : String should be Int
import lua.Table;
class Main {
static public function my_test(): Table<String, Int>
{
var t: Table<String, Int> = Table.create();
t["test"] = 1; # Heres the problem
return t;
}
static public function main(): Void
{
var x = my_test();
trace(x);
}
}
If I change the problematic line to:
t[1] = 1;
It strangely doesn't complain which seems not logical to me as it's a wrong type in my understanding.
If I put $type(t)
somewhere into the snippet above, it correctly identifies it as lua.Table<String, Int>
.
I looked into source of std/lua/Table.hx
and based on some code there I used untyped
keyword in my snippet: t[untyped "test"] = 1;
only then it generates desired lua code that executes successfully:
Main.my_test = function()
local t = ({});
t.test = 1;
do return t end;
end
Although I expected something like:
Main.my_test = function()
local t = {};
t["test"] = 1;
do return t end;
end
So, why I have to use this keyword?
$ haxe --version
4.2.1+bf9ff69
# I compile with
$ haxe -D lua-vanilla --main Main --lua Main.lua
This seems to be a limitation of Haxe and how the external library is defined as an extern
instead of wrapped in an abstract. In std/lua/Table.hx, Table<A, B>
is defined as implementing ArrayAccess<B>
. This tells Haxe that the type supports indexing into it with integers and that the element type is B
. There is no way for it to indicate that the allowed indexer type is A
. ArrayAccess<T>
’s documentation states:
This interface should be used for externs only. Haxe does not support custom array access on classes. However, array access can be implemented for abstract types.
Instead of defining lua.Table
as a simple extern
in this way, you might be able to define a wrapper around it as an abstract. That would let you specify an array setter/getter appropriately. An example of doing this is below.:
extern class LuaGTable<A, B> implements ArrayAccess<B> {
}
abstract MyLuaTable<K, V>(LuaGTable<K, V>) {
inline public function new() {
this = untyped __lua__("({})");
}
@:arrayAccess
public inline function set(k: K, v) {
this[untyped k] = v;
}
@:arrayAccess
public inline function get(k: K) {
return this[untyped k];
}
}
The use of implements ArrayAccess<B>
is required to permit use of the indexing operator in the expression this[untyped k]
at all. What Haxe thinks we’re doing is supplying an integer value to the indexing expression. The abstract itself provides the K
/V
-typed indexer.
Why this isn’t the approach taken by the Haxe standard library is something I do not know. You could probably find or file a bug at their GitHub repository to start a discussion on this. I don’t think making Table<A, B>
hard to use was intentional.