ArrayAccess by string on an unknown or dynamic 类型

ArrayAccess by string on an unknown or dynamic type

我使用一些更高级的东西(例如 haxe 中的抽象)构建了自己的 Yaml 解析器,以便更好地理解一切是如何工作的,但我又碰壁了。

重要的部分是摘要,abstract YamlMap (StringMap<Either<String, YamlMap>>),它充当我的数据的容器。 "node" 可以是字符串或另一个 yamlmap,位于树的更深处。

这个 YamlMap 有多种获取数据的方法,例如 getMap(key):YamlMapgetString(key):String,以及一个动态访问方法 getDynamic(key):Dynamic

不幸的是,似乎只有一个 @:arrayAccess 可以工作是抽象的,或者我遗漏了一些东西。似乎你不能 "arrayAccess" 带有字符串的动态对象,或者至少编译器阻止我这样做。

所以,这有效:data.getMap('test_node').getMap('sub_node1').getString('value2') 但这不是:data['test_node']['sub_node2']['value2']

如果我将 getDynamic 设置为 arrayAccess,它告诉我 sub_node2 应该是一个整数。但是,如果我将 getMapgetString 都设置为 arrayAccess,它将始终调用第一个 arrayAccess 标记的方法。所以它要么在尝试获取 "value" 时失败(这是一个字符串,但代码试图获取一个映射)或者不编译,因为我猜,它试图从字符串中访问一个字符而不是地图位置。

所以,我的猜测是,与此相关manual entry,对任何非抽象对象的 arrayAccess 都被锁定为 int,因此拒绝使用字符串访问动态对象。

我能想到的一个可能的解决方案是 return 某种抽象,它将 "flatten" 转换为正确的类型,而不是使用动态值。是否有任何其他方法可以在动态地图上实现字符串化数组访问?

注意:在某种程度上,这是出于好奇,因为对映射和字符串进行不同调用的当前方法足以满足日常使用。我也知道现有的 yaml haxelib,但这是一次学习经验,因为它试图取代有时可能有问题的 haxelib。

这里有一个 Pastebin YamlMap 摘要供感兴趣的人使用。

首先,一个抽象 可以 有多个 @:arrayAccess 方法:

abstract MultiArrayAccess({}) from {} {
    public function new() this = {};
    @:arrayAccess function getInt(i:Int) return i;
    @:arrayAccess function getString(s:String) return Std.parseInt(s);
}
var i = new MultiArrayAccess();
trace(i[1]); // 1
trace(i["3"]); // 3

这与您的示例之间的区别在于键类型不同。这是必要的,因为对抽象(和一般抽象)的数组访问是一个 编译时 特性。这两个数组访问的 AST 转储如下所示:

_Main.MultiArrayAccess_Impl_.getInt(i, 1)
_Main.MultiArrayAccess_Impl_.getString(i, "3")

因此,编译器必须知道在编译时调用哪个方法。这在您的情况下是不可能的,因为两种方法的键类型都是 String 。通过将 getString()getMap() 替换为 [],您正在丢失信息。


It also seems that you cannot "arrayAccess" a dynamic object with strings, or at least the compiler is preventing me from doing it.

没错,Dynamic 上不允许数组访问。但是,标准库有一个抽象叫haxe.DynamicAccess,是用Reflect.field()实现数组访问的抽象。当然,这会导致运行时开销。您可以有一个完全动态的实现基于:

typedef YamlMap = haxe.DynamicAccess<YamlMapValue>;

abstract YamlMapValue(YamlMap) from YamlMap {
    @:arrayAccess function get(key:String):YamlMapValue {
        return this.get(key);
    }

    @:arrayAccess function setMap(key:String, value:YamlMap) {
        Reflect.setField(this, key, value);
    }

    @:arrayAccess function setString(key:String, value:String) {
        Reflect.setField(this, key, value);
    }
}
var data = new YamlMap();
data["test_node"] = new YamlMap();
data["test_node"]["sub_node1"] = new YamlMap();
data["test_node"]["sub_node1"]["value2"] = "foo";

var map:YamlMapValue = data["test_node"]["sub_node1"];
trace(map["value2"]); // foo

但是,这不一定是个好主意,因为这样会损失很多类型安全性(以及性能)。