HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit
https://haxe.org
6.17k stars 654 forks source link

[Lua] (Edit) MultiReturn types should not be used as type params #6510

Closed GerardSmit closed 7 years ago

GerardSmit commented 7 years ago

The HaxeIterator doesn't support multiple returns, for example:

package oc.component;

import lua.NativeIterator;

@:luaRequire("component")
extern class Component {
    public static function list(): NativeIterator<ListItem>;
}

@:multiReturn extern class ListItem {
    var address : String;
    var type : String;
}

In this extern declaration I have the function component.list() which returns an iterator with multiple return values. However, the HaxeIterator doesn't support this since the state is only storing the first return:

public function next(){
    var ret = this.state;
    this.state = this.f();
    return ret;
}

As workaround I resolved this with table.pack and table.unpack:

package lua;

class PackHaxeIterator<T> {
    var state : Table<Int, Dynamic>;
    var f : Void->T;

    public function new(f:Void->T){
        this.f = f;
        this.state = TableTools.pack(f());
    }

    public function next(): T {
        var ret = this.state;
        this.state = TableTools.pack(this.f());
        return TableTools.unpack(ret);
    }

    public function hasNext() return TableTools.maxn(state) > 0;
}

This has been tested in Lua 5.2, however this causes the iterator to create a table every next.

jdonaldson commented 7 years ago

Sadly, I can't support this type of iterator right now. This should in fact be an error, so I'll hold this bug open for adding an error message.

The main problem is that the iterator method needs to save the state of the component.list call and return it. This is because we use a next/haveNext setup for our iterators. We need to call the iterator to determine if it is not null (and if it hasNext). This means we need to save the result of a multireturn function internally in the iterator.

The current multireturn support does not allow returning multireturn values in a Haxe code function (as would be the case in the "next" function).

The reason this isn't supported is that the multireturn type is not a "true" type according to the compiler. Passing it as a return argument will cause the compiler to lose track of its special status outside the invoked function. Basically, you're only supposed to use multireturns where you've declared the return variables that contain their result.

So, how to solve multireturn iteration? I would prefer to hack the "for" loop behavior in lua for this context. However, I can't do that either because the compiler doesn't emit for-loops in the AST (it uses while loops instead, which doesn't have the nice multireturn behavior we need). We can consider this as one useful use case for for-loop nodes in the Haxe AST... maybe for Haxe 4? cc @Simn

This is unfortunately pretty messy, it may be worth writing some sort of wrapper function for this:

class Main {
    public static function main() {
      var f = function(address:String, ?type:String){ // necessary to make the second arg optional
        if (address == null) return false;
        trace(address + " is the value for address");
        trace(type + " is the value for type");
        return true;
      }

      while(f(untyped Component.list())){
        // don't actually need this.
      }

    }
}
GerardSmit commented 7 years ago

I think I leave it how it is now then, the PackHaxeIterator (the class that I wrote in the first message) is a workaround for now.