ArrayAccess由未知或动态类型的字符串组成

时间:2017-08-29 12:42:28

标签: abstract haxe

为了更好地了解一切是如何工作的,我使用了一些更先进的东西,例如haxe的摘要,构建了我自己的Yaml Parser,并且我已经打了另一面墙。

重要的部分是一个抽象的abstract YamlMap (StringMap<Either<String, YamlMap>>),它充当我数据的容器。 “节点”可以是字符串,也可以是另一个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应该是一个int。但是,如果我将getMapgetString都设置为arrayAccess,它将始终调用第一个arrayAccess标记的方法。因此,当尝试获取“值”(这是一个字符串,但代码试图获取一个映射)或者没有编译时它要么失败,因为,我猜,它试图从字符串而不是映射中访问字符位置。

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

我能想到的一个可能的解决方案是,而不是使用动态值来返回某种抽象,这种抽象会在投射时“扁平”为正确的类型。是否还有其他方法可以在动态地图上实现字符串化数组访问?

注意:在某种程度上,这是出于好奇,因为对不同的地图和字符串调用的当前方法适用于日常使用。我也知道现有的yaml haxelib,但这是一次学习经历,因为它试图取代有时会出错的haxelib。

这是YamlMap摘要的Pastebin,适合所有感兴趣的人。

1 个答案:

答案 0 :(得分:2)

首先,摘要可以有多个@: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()替换为[],您的信息就会丢失。

  

似乎你不能使用字符串“arrayAccess”动态对象,或者至少编译器阻止我这样做。

这是正确的,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

但是,这不一定是个好主意,因为你会以这种方式失去很多类型安全性(因此,性能)。