ceylon / ceylon-js

DEPRECATED
Apache License 2.0
54 stars 9 forks source link

enhancement to dynamic object #474

Open ghost opened 9 years ago

ghost commented 9 years ago

in javascript there are two ways to define property names in object literals one is using with quotes and other is without like this

var header = {
  date: 123,
 "Content-Type" : "text/plain"
}

in ceylon i can't create this object because of this second property.There is heavy usage of this type of property in nodejs app since most of http header names have "-" in between them like "content-length" i tried this but not working

dynamic header = dynamic [date = 123, "Content-Type" = "text/plain"; ];

is there any way??

gavinking commented 9 years ago

Thoughts, @chochos?

chochos commented 9 years ago

It's true, those are valid keys in js. But they're only accessible with k[v] syntax. We'd need syntax not only to create objects with those keys, but also to read and assign them (can't remember right now what we do with k[v] when k is native, but that's easu; the problem is there's no o[k]=v syntax in ceylon)

gavinking commented 9 years ago

Hrm. Tricky one.

gavinking commented 9 years ago

@chochos So I think the simplest solution would be to just add putAttribute() and getAttribute() methods to Object.prototype, so that the above code could be written as:

dynamic header = dynamic [ date = 123; ];
header.putAttribute("Content-Type", "text/plain");

We could even support the following, in addition to putAttribute() and getAttribute():

dynamic header = dynamic [ date = 123;  attributes = { "Content-Type"->"text/plain" }; ];
gavinking commented 9 years ago

Or instead of putAttribute() and getAttribute(), we could add an attributeMap attribute to Object.prototype, letting you transform any JS object to a Ceylon Map<Object,Anything>.

dynamic header = dynamic [ date = 123; ];
header.attributeMap.put("Content-Type", "text/plain");
gavinking commented 9 years ago

Of course, I guess the natural syntax for this would be:

dynamic header = dynamic { date = 123; "Content-Type" -> "text/plain", "Content-Length" -> 348 ];
String contentType = header["Content-Type"];

That would require a language change, but it's doable. We would have dynamic { ... } and dynamic [ ... ] with slightly different semantics.

chochos commented 9 years ago

The attributeMap wouldn't work because put is not a method of native JS objects. We'd have to transform the put call into o[k]=v since that's the only way to add properties to a native JS object, but that would make it a nuisance to call a real put method in JS objects (have to call it as o.\iput(...).

I don't see why we need to add a different syntax with entries to the dynamic objects; why not just allow quoted keys? dynamic[date=123; "Content-Type"="bla", "foo-bar"="baz"]

gavinking commented 9 years ago

why not just allow quoted keys?

Grumble. because it looks like you're assigning to a string.

chochos commented 9 years ago

well, you are assigning to a string, in a way; keys in JS objects are strings...

luolong commented 9 years ago

This actually begs for a question if we need syntax in Ceylon for addressing weird identifiers.

Some languages (Groovy for example), allow defining your method names in double quotes, allowing all kinds of weird and groovy names that can contain whitespace, special characters and unicode symbols not otherwise allowed for identifiers.

This is sometimes quite neat; for example when writing test cases, you can write very descriptive method names, that read well later in the test reports...

In any case, this issue is just one of these cases that shows how we need this for interop with other languages that are more liberal with identifier names than Java or Ceylon.

So I have two potential ideas of how this would work:

1) Have special quoting around the identifier, that allows use of non-identifier characters:

dynamic header = dynamic [date = 123, \i'Content-Type' = "text/plain"; ];

2) Just add a way to escape characters in the identifier with a backslash. Uglier, but I think that if the goal is to allow referencing identifiers from another language, then making those identifiers look nice is a non-goal any way...

dynamic header = dynamic [date = 123, \iContent\-Type = "text/plain"; ];
gavinking commented 9 years ago

@luolong This is a good point. The issue is, as always, whether we can think of any non-disgusting syntax for this, now that we've already used all the reasonable quote characters for other things.

luolong commented 9 years ago

What do you think of the proposals in my previous post?

gavinking commented 9 years ago

What do you think of the proposals in my previous post?

I'm not sure I do think anything yet. I guess the second one might be on the right track.

What would be wrong with simply Content\-Type?

chochos commented 9 years ago

I don't see anything wrong with Content\-Type but does this mean \ will be a general escape character? Because in JS you can have any char as a property name: a.b, a,b, a-b, a;b, a!b, a$b, a?b etc are all valid property names (although most are only accesible via o[k] syntax).

gavinking commented 9 years ago

I don't see anything wrong with Content\-Type but does this mean \ will be a general escape character?

Wellyeah, I suppose, is there some reason why it wouldn't work?

gavinking commented 9 years ago

Another option that might work could be to deprecate \ifoo and \Ifoo and use:

 i\Content-Type\

or

i'Content-Type'

or even

i`Content-Type`

Though we already use backticks for too many things.

chochos commented 9 years ago

I think it would be better to use \ as a general escape char. these i'x' are just... eww...

luolong commented 9 years ago

What would be wrong with simply Content\-Type?

Nothing, except that if Content-Type is a name for a field/method of an object, then \i prefix is mandated by Ceylon language rules.

I actually quite like the backslash escape syntax. This way I can write a method name like this

shared object windowsFolders {
    shared String \iProgram\ Files = "Program Files";
}

And it would actually look rather clean...

Another question is -- what it would be escaping? Same rules as within String literal?

FroMage commented 9 years ago

I personally think we should deprecate \i and friends, especially if we use them for non-interop, and go away from single letters and use something like type::foo or value::'Content-Type' or something similar. Using backslash is confusing and repetitive for names like Content-Type-Negociation-Timeout where we'd have to use many. Quoting is clearer to read, and full names makes it way less cryptic. But all this is a discussion that belongs in spec.