Skip to content

Conversation

deltaluca
Copy link
Contributor

var obj:{"var":Bool} = {"var":true};
trace(obj."var");

i understand @Simn has said there was 'some' reason this sort of thing hasn't been added before, and whether the exact syntax would be wanted or not is to be decided.

However, even if we reject the obj."var" syntax (Although this is 'so' much better than Reflect use, especcialy since the lifting of the type restriction means such usage is staticly checked now), having the non-haxe-ident fields as part of the type is surely better than allowing things like:

var obj = {x:10};
obj = {x:10, "other_field": 20, "hahah you suck": 30 };

going through compile because the non-haxe idents are just 'not checked'.

The main usage for this sort of thing, like with json syntax itself is for working with js target, in my specific case, providing typed externs for a lib which uses non-haxe idents for object fields, and without this there's just no way to type it nicely without changing the field name and providing a horrible wrapper to then assign the real field using untyped js... ew.

var obj:{"var":Bool} = {"var":true};
trace(obj."var");
@fponticelli
Copy link
Contributor

+1 ... it happened to me way too often to have to work around the issue.

@Simn
Copy link
Member

Simn commented Sep 19, 2013

I'm quite sure this is going to require some generator modifications as some generators handle escaping of target keywords only if they are not haxe keywords.

I'm still in favor of this change.

@ncannasse
Copy link
Member

I'm against it. JSON notation is just a convenience to be able to copy/paste JSON into Haxe. I don't think we should allow any additional support for it as part of the type system. This would blur the difference between an actual object and a String Map.

@ncannasse ncannasse closed this Sep 19, 2013
@Simn
Copy link
Member

Simn commented Sep 19, 2013

I won't try to argue against the blurring as I consider that rather subjective. I just don't see the value of forsaking all type-safety by using Reflect.field just because an external field happens to be named like a haxe keyword.

@waneck
Copy link
Member

waneck commented Sep 20, 2013

Nicolas, if that's your worry, can we maybe make a difference then between what's "JSON" notation and what's simply trying to access a field that is a haxe keyword? I think it's a fair compromise.

@ncannasse
Copy link
Member

I don't think we should introduce a new specific syntax for accessing fields that are invalid in haxe (either keywords, or special chars), Reflect.field is enough IMHO

@deltaluca
Copy link
Contributor Author

deltaluca commented Sep 20, 2013

Can we please split this discussion in two then, my changes are really two seperate things.

On one side, there are the extensions that allow non-haxe identifiers to be represented in the type of an anon-object, which is really a much more important one.

var obj = { "dynamic": true };
$type(obj); // Warning: {}
obj = { }; // compiles without error
obj = { "static": false }; // compiles without error

And the real-life use case. writing externs for non-haxe libraries:

extern class Example {
    static function example(params: { "dynamic": Bool ... }) : SomeReturnType;
    static function anotherExample() : { "dynamic": Bool };
}

there is no 'good' way of doing things like this, exactly because of the way non-haxe identifiers are not represented in the type system, there's really only two ways of doing it:

extern class Example {
    static function example(params : {}) : SomeReturnType;
    static function anotherExample() : {};
}

which clearly is terrible, you completely lose the type information, you have no way of knowing from code hints, or from compiler errors that the params argument is an object with a field called dynamic, and there is no way of knowing what fields the return of anotherExample has.

The alternative I presently use is:

extern class Example {
   static inline function example(params : {_dynamic:Bool}):SomeReturnType return untyped __js__("(function (parms) { params.dynamic = params._dynamic; return this.example(params); })").call(this, params);
   static inline function anotherExample(): { _dynamic: Bool} return untyped __js__("(function () { var ret = this.anotherExample(); ret._dynamic = ret.dynamic; return ret; })").call(this);
}

which apart from being completely specific to JS, produces some really horrible looking, and totally unoptimisable code, but is the only nice way of getting static typing for the haxe code. This of course assumes the external code isn't going to fail because of the extra _dynamic field on the object etc.


On the other side of my changes, is having a way to then, without using Reflection which comes with no static type checks and again, produces sub-optimal code, to access fields of these externally provided objects that do not correspond to valid haxe field names.

var obj = Example.anotherExample();
trace(Reflect.field(obj, "dynamics")); // compiles... oops there is no field called dynamics. Sigh.

compared to

var obj = Example.anotherExample();
trace(obj."dynamics"); // Error, obj has no field named dynamics

I understand this syntax would be new, an alternative new syntax would be from C# using the @ modifier to have

trace(obj.@dynamic);

as a means of accessing a field that is a reserved word 'in haxe'. Personally I feel this isn't as good, because it still restricts us to what haxe considers a valid identifer in terms of things like only containing a-zA-Z0-9_ which may not be the case for the external library. It is also 'completely new' syntax, as opposed to obj."dynamic" which is paired up nicely with the existing json syntax var obj = { "dynamic": true }; and with my type changes also: var obj:{"dynamic" : Bool} = { "dynamic": true }; An example, being that JS permits things like $ in identifiers, and if an external library used such a field name, then use of C# @ syntax wouldn't work.

@deltaluca
Copy link
Contributor Author

@Simn I made some changes on my branch (Don't seem to reflected here after this request was closed, but they are there on my branch) for genphp, genjs and genas3 to support this too. No changes were needed for swf/c++/c#/java since access on anon-objects already goes through a level of indirection

@fponticelli
Copy link
Contributor

I support Luca on all the line. The pain he describes is real for whoever
works integrating Haxe with third party libraries like I do frequently.
Reflect simply doesn't cut it.

On Fri, Sep 20, 2013 at 6:08 AM, Luca Deltodesco
notifications@github.comwrote:

@Simn https://github.com/Simn I made some changes on my branch (Don't
seem to reflected here after this request was closed, but they are there on
my branch) for genphp, genjs and genas3 to support this too. No changes
were needed for swf/c++/c#/java since access on anon-objects already goes
through a level of indirection


Reply to this email directly or view it on GitHubhttps://github.com//pull/2185#issuecomment-24805897
.

@waneck
Copy link
Member

waneck commented Sep 20, 2013

I also agree with Luca. We should make the external library integration as painless as possible. Actually the "@" syntax for C# had the same purpose - since C# is only one of many CLR language implementations, there should be a way to easily access an identifier that is invalid from C# but valid on another language.

@frabbit
Copy link
Member

frabbit commented Sep 20, 2013

Same here, its always a pain when dealing with such situations. Of course if you only write everything in haxe that's not a big deal. But the reality is that you don't won't to reinvent the wheel when a native lib is already available. Adapting to such kind of problems is very important for haxe.

@frabbit
Copy link
Member

frabbit commented Sep 20, 2013

btw. my feature request #1492 would also simplify working and defining interfaces for external libraries.

@frabbit
Copy link
Member

frabbit commented Sep 20, 2013

would at least a metadata based solution be an option?

typedef MyJson = {
   @:native("var") var var1:String;
   @:native("some-key") var someKey:Int;
}

the definition is not pretty but using it would be straightforward:

var x:MyJson = { var1 : "whatever", someKey : 1 }

this would also mean that the native metadata becomes part of the type, because MyJson is not compatible to:

typedef Foo = {
   var var1:String;
}

because var1 is missing the @:native("var") metadata. But it would be compatible to:

typedef Foo = {
   @:native("var") var var1:String;
}

i'm sure this solution would also have some problems, but it's just an idea...

@jonathanhardie
Copy link
Contributor

+1, I've described this same pain in #2164

@ncannasse
Copy link
Member

Yes, @:native would work better here. Might require a "cast" for the JSon though, although we might be able to deal with it in some specific cases.

@ncannasse ncannasse reopened this Sep 21, 2013
@Simn
Copy link
Member

Simn commented Sep 21, 2013

While I'm glad for any kind of solution, the @:native one looks vastly inferior to what luca suggests.

@frabbit
Copy link
Member

frabbit commented Sep 21, 2013

Yes, just for the json naming problem it doesn't looks nice, it adresses different problems which somehow overlaps with this specific one.

@sledorze
Copy link

On one side; @:native is the solution to naming problems with externs and looks like the de facto solution.
However I'm not sure how to treat unification between anon types; it would requier to check for the @:native metadata but doing so means that non external anons that we requier to unify with external one would requier also this @:native metadata.. (if you see what I mean..) - But maybe I have not thought enough about that.

I also like the lean approach deltaluca proposed but I see it as inconsistent with the rest of the renaming solutions in the lang and also I'm not sure it would not break a lot of ide tooling support regarding non standard member names.

So, in the end, I'm not sold yet on any solution - any arguments?

@ncannasse
Copy link
Member

I'm closing this one, please add a @:native proposal or even better pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants