COMMUNITY

How to properly abstract over a class implementing a self-referring method?

Apologies for the horrendous question title, I couldn’t really figure out what to call this.

Part of something I’m writing is an abstract over a class, with the class implementing an interface containing a method having return type of that interface.

A short example, Main.hx:

package;

class Main {
	static function main() {
		var a:MyArray<Int> = [22];
		var b = a.copy();
		trace("catch-" + b[0]);
	}
}

interface Container {
	function copy():Container;
}

@:forward @:access(_MyArray)
abstract MyArray<T> (_MyArray<T>) {
	@:from public static function from_Array<T>(arr:Array<T>):MyArray<T> {
		var array = new MyArray<T>();
		array.data = arr;
		return array;
	}

	public function new() {
		this = new _MyArray<T>();
	}

	@:arrayAccess public function get(index:Int):T {
		return this.data[index];
	}

	@:arrayAccess public function set(index:Int, value:T):T {
		return this.data[index] = value;
	}
}

private class _MyArray<T> implements Container {
	var data:Array<T>;

	public function new() {
		data = new Array<T>();
	}

	public function copy():_MyArray<T> {
		var copy = new _MyArray<T>();
		copy.data = data.copy();
		return copy;
	}
}

Interface Container basically makes anything that implements it capable of copying itself. _MyArray is some class meant to be randomly accessible, abstracted by MyArray implementing that random accessibility and allowing assignments from array literals.

The problem I’m running into is, when compiling (Haxe 4.0.0-rc.5), this error occurs:

Main.hx:7: characters 9-10 : Array access is not allowed on _Main._MyArray<Int>
Main.hx:7: characters 9-10 : For function argument 'v'

Fair enough, _MyArray.copy returns a type of _MyArray which can’t be accessed using array notation. If I change the return type of _MyArray.copy though…

Main.hx:47: lines 47-51 : Field copy has different type than in Container
Main.hx:14: characters 2-28 : Interface field is defined here
Main.hx:47: lines 47-51 : error: MyArray<_Main._MyArray.T> should be Container
Main.hx:47: lines 47-51 :  have: (...) -> MyArray<...>
Main.hx:47: lines 47-51 :  want: (...) -> Container

which makes a bit less sense and is the part I’m currently stuck on.

All these issues can be resolved by adding an implicit from cast to the MyArray abstract and explicitly typing variables being copied into like so:

class Main {
	static function main() {
		var a:MyArray<Int> = [22];
		var b:MyArray<Int> = a.copy();
		trace("catch-" + b[0]);
	}
}
...
@:forward @:access(_MyArray)
abstract MyArray<T> (_MyArray<T>) from _MyArray<T> {
	...
}
...

But that seems a bit clunky and I’d really prefer another way to work around the issue.

I’m using Haxe 4.0.0-rc.5, and here is an interactive version of the above code (but in Haxe 3.4.4).

Thanks

Array access is allowed only on abstracts. So you need to add copy to your abstract

    public inline function copy():MyArray<T> {
        return cast this.copy();
    }

returning abstract, not underlying type…

Wonderful, thank you!