RealyUniqueName / haxe

Haxe - The Cross-Platform Toolkit

Home Page:http://haxe.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

NativeStructArray

mockey opened this issue · comments

Just wanted to write down my ideas on associative arrays. Not sure if it makes sense to implement this right away or if it makes sense at all.

From my POV associative arrays in PHP are often used in places where you would use an anonymous structure in Haxe, e.g. in option parameters for functions. There are many named fields (often optional) with a value of a certain type. So it makes sense to me to be able to define their type as a struct, e.g.:

typedef Options = {
  opt1:Int,
  ?opt2:String,
  opt3:Bool
}
var opts:NativeAssocArray<Options> = {opt1: 1, opt3: true};

IMO being able to define such structs and have the compiler check them is a huge benefit when interfacing with native PHP functions.
Checking the type would be basically be checking if the type param unifies with the assigned value, no? I wrote a macro once which does something like this.
Of course it would be good to generate a native array for literals like this:
['opt1' => 1, 'opt3' => true]
without going over objects. But I saw that you generate structs like this already (and just cast them to (object).

One open question is how to make a difference to map-like arrays with "dynamic" keys, like in Haxe this would be Map<String,Option>. Maybe one could misuse T:, like NativeAssocArray<T:Options> or maybe better define an additonal type like NativeStruct<T> for these struct-like arrays?

Implemented in HaxeFoundation@395b870

I like the idea of NativeStruct. Perhaps with more "explaining" name.
We can define an abstract over NativeAssocArray like this:

@:forward
abstract NativeOptionsArray<T:{}>(NativeArray) to NativeArray {
  @:from static inline function fromOptions(options:T) 
      return untyped __php__('(array)', options);  
}

//Extern example
@:native('Zend\Http\Client')
extern class ZendHttpClient {
  public function new (url:String, options:NativeOptionsArray<HttpClientOptions>);
}

typedef HttpClientOptions = {
  ?timeout:Int,
  ?keepalive:Bool,
  //<...>
}

//Usage
var http = new ZendHttpClient('http://example.com', { timeout:60 });

OK, you got the concept.
NativeOptionsArray is not the best name for it IMO, because it can be used for more than options. I use it for defining types sent and returned by the SoapClient e.g. But I could live with that name.
A special handling of the type and its parameter should be no problem to add to the generator I guess.
Also I would like to avoid some double casting like (array)(object)['timeout' => 60] and generate ['timeout' => 60] directly. Besides the generator has to deal with recursive structures I guess.

NativeOptionsArray is not the best name for it IMO

Do you have a better name to suggest? Maybe NativeStructArray? I would like to keep words "native" and "array" so that users know these structures are not Haxe objects.

I would like to avoid some double casting like (array)(object)['timeout' => 60] and generate ['timeout' => 60] directly

I gues this could be done for direct assignments of object declaration to identifier of NativeStruct type. It should be quite easy for the top-level of object declaration. But dealing with recursion here can complicate things a lot.

Do you have a better name to suggest? Maybe NativeStructArray?

Yes, I like that a bit better.

I would like to keep words "native" and "array" so that users know these structures are not Haxe objects.

Sure, seems reasonable.

I gues this could be done for direct assignments of object declaration to identifier of NativeStruct type.

Yes, that's what I mean. Only literal assignments, as in your example.

It should be quite easy for the top-level of object declaration. But dealing with recursion here can complicate things a lot.

You have to deal with recursion also in a standard object declaration, don't you?
In my macro I use the simple rule that any object declaration inside a NativeStruct gets generated as native array, too. But the more correct way would be typing every underllying NativeStruct as NativeStruct as well, which is not so much extra work to the amount of work you have for declaring types anyway.

Wow, cool, have to try this, I guess :-)