ceylon / ceylon-js

DEPRECATED
Apache License 2.0
54 stars 9 forks source link

JavaScript Interoperability #72

Open ikasiuk opened 12 years ago

ikasiuk commented 12 years ago

We currently have JS interop for the browser DOM objects in ceylon-web-ide-backend. This works by defining a Ceylon version of the types' interfaces with annotation nativejs and implementing a JS module which makes these JS types available as Ceylon types. The compiler has special treatment for types with nativejs annotation so that it can access them in the correct way.

We should make this principle applicable to any native JS class. It should be possible to let the compiler automatically generate the JS glue code from the Ceylon type definitions. So to use a native JS class from Ceylon you would describe the interface of that class as a Ceylon type and annotate it nativejs. The compiler would then automatically generate the necessary glue code from that Ceylon file, enabling you to use the native JS class as if it was a Ceylon class.

We will probably have to implement special handling for some identifier names to avoid collisions of native JS members with Ceylon members. Such special handling could also enable us for instance to merge ceylon.language.String with the builtin JS String class.

FroMage commented 12 years ago

Surely this is for M5?

ikasiuk commented 12 years ago

yes

FroMage commented 11 years ago

Also see http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.Dynamic

chochos commented 11 years ago

So I was thinking about top-level methods, especially for interop, and I thought that Dynamic could have an optional String in its initializer; any string passed there would be used instead of the regular object constructor, like this:

Object x = Dynamic(); //generates var x$blabla = {};
Callable<Dynamic,String> require = Dynamic("require"); //generates var require$blabla = require;
Callable<Dynamic,Dynamic> jquery = Dynamic("$"); //generates var jquery$blabla = $;
Dynamic elems = Dynamic("$('span#css')"); //generates var elems$blabla = $('span#css');

Of course this would open the door for invalid code generation which would only be detected at runtime, if invalid js code is put inside the Dynamic (such as an unclosed quote, parens, etc). Maybe the compiler can warn if it detects something that looks like invalid code inside a Dynamic call, and anyway this could solve the interop problem. WDYT @gavinking ?

quintesse commented 11 years ago

Dunno, this sounds very hacky to me. Also remember that Dynamic might be used by the Java backend in the future.

gavinking commented 11 years ago

@chochos I think a better way would be a toplevel object named dynamic of type Dynamic, so you would write:

Callable<Dynamic,String> require = dynamic.require;

I don't think we should be sticking JavaScript code in strings.

gavinking commented 11 years ago

However, I'm beginning to view Dynamic itself as a rather inelegant solution, and I'm looking for other options. I think it would be better to have a specific syntax to suppress typechecking rather than a special Dynamic type. One option would be to add operators for "reflective" member access:

~require(something).~someFunction()

Along with an operator to force assignability of Anything to an arbitrary type:

Integer x = ~require(something).~someFunction()! + 1;

But really code with a bunch of ~s and !s is just not going to like like either Ceylon or JavaScript, it's just going to look awful. So I think a better answer would be a dynamic block or dynamic annotation:

Integer x;
dynamic {
    x = require(something).someFunction() + 1;
}

The type of require() and someFunction() would be Anything. The type of require(something).someFunction() + 1 would also be Anything.

quintesse commented 11 years ago

Well that's a bit how the nativejs annotation works, you put it on a toplevel class or method and the JS compiler will generate normal JS accessors, so no Ceylon-specific name mangling at all. In the same manner we could have an annotation that suspends all typechecking on members of that type.

I still think it's a pretty heavy-handed solution that people in general should try to reserve for those cases where no other solution will do (for example dynamic objects that could have a different set of members depending on circumstances).

gavinking commented 11 years ago

@quintesse Huh? What you just typed didn't make any sense.

How do you propose to let the typechecker know that it shouldn't produce an exception when I reference a member it doesn't know about, or try to apply an operator to something whose type it doesn't know?

chochos commented 11 years ago

@quintesse The nativejs annotation is useful when you want to make typesafe interop, such as the browser module you wrote for the web IDE; this dynamic block allows you to disable type safety (and use undeclared stuff), so this would allow you to use for example the require function in js, or call jquery stuff (still don't know how though, since the jquery function name is just $), etc.

chochos commented 11 years ago

Moving to M6

quintesse commented 11 years ago

Moving to 1.0

chochos commented 11 years ago

Moving to 1.1