jsx / JSX

JSX - a faster, safer, easier JavaScript
http://jsx.github.io/
MIT License
1.46k stars 102 forks source link

compile-time foreign import #177

Open nyuichi opened 11 years ago

nyuichi commented 11 years ago

Extending import statement to load native foreign objects such as JS files and so objects if the c++ emitter is implemented. The syntax will be like

foreign import "foo.js";
foreign import "bar.so";

After loading foreign files, you can write native class declarations of the imported objects at the bottom of the import statements. So the compat file would have a form like

<foreign imports>
<imports> // if you have to do

<native class decl>
<native class decl>
<native class decl>
...

Code imported with foreign import statement will be loaded at compile time: in other words, it has static linkage. This is in contrast with native syntax which evaluates fragments of js code at runtime. And it is easily extensible and supports not only js files but also shared objects and in the future we can add support for any other file formats.

kazuho commented 11 years ago

I wonder if the proposed syntax can cover real-world use cases in JavaScript.

Some JavaScript source files export functions into the current namespace, while every common.js-style script export their symbols into the export variable expecting the application code to assign the contents to some other variable.

A declarative syntax like the proposed cannot handle such variety. IMO we should provide a way to code arbitrary expression (or even statements) in foreign programming language, and therefore I think this issue should be solved as part of #169.

nyuichi commented 11 years ago

I expect that files imported with this syntax should be stub files newly written for foreign import. What I want to do is to hide all implementation details behind the stub files. I believe that every native class is an interface declaration via which we access js objects, and it should not contain dirty complicated js implementation. That's not a definition, but a prototype. I do put emphasis on keeping jsx code clean more than on keeping programming easy. This is why I love this idea much more than #169.

kazuho commented 11 years ago

@wasabiz

I do put emphasis on keeping jsx code clean more than on keeping programming easy.

Your idea is understandable. But some (if not most) of the native bindings are inherently dependent to the native programming language (e.g. web.jsx cannot only be provided for the JavaScript binding). And for such cases, having a neat way to attribute native bindings with the underlying programming language makes sense.

The proposal I wrote on https://github.com/jsx/JSX/issues/169#issuecomment-18602503 (the link points directly to my comment on the issue) does not introduce any heavy modifications to the JSX programming language while providing enough power for the users of the language to write native bindings easily.

nyuichi commented 11 years ago

@kazuho

having a neat way to attribute native bindings with the underlying programming language makes sense.

I don't think so. There are too many demerits. I think

  1. Native class is interface. It should not contain any implementations.
  2. Injected native code will be absolutely unmaintainable. The suggestion in #169 is that injecting native code by using strings. That means that all inserted code wouldn't be able to get any checks by the compiler, the editor, and other third-party tools. JSX claims that it provides not only the compiler but also the build environment including documentation tools, completion support and other things. But the suggestion only breaks maintainability. If we do that with stub files, we are free to use tools like jslint or google closure compiler against the stub files.
  3. It's even easier to introduce foreign import than #169. Change to the parser will be only one, and the only thing emitter has to do will be read contents from stub files to write them out in the head of the output code.
kazuho commented 11 years ago

@wasabiz

  1. Native class is interface. It should not contain any implementations.

No. It is a binding. In some cases it is possible to define a binding without writing native code at all (an example of such case would be the built-in Object type), but in other cases you need to define both the interface for JSX and the binding in native code.

Moving the native code outside of the JSX source code does not remove complexity. It just changes where the binding exists.

  1. Injected native code will be absolutely unmaintainable. The suggestion in #169 is that injecting native code by using strings.

No. First, the proposal allows the users to define the binding in a separate file (quote: some mechanism to load external data as a string literal). Second, in cases where the definition of the binding could be short (e.g. common.js), it would actually be easier to read / write / maintain if such bindings could be defined inline than enforcing them to be written in a separate file.

  1. It's even easier to introduce foreign import than #169. Change to the parser will be only one, and the only thing emitter has to do will be read contents from stub files to write them out in the head of the output code.

I agree that it would be easier for us (the developer of the JSX compiler). But it would not be easier for the users. In the proposal I made in #169, a native binding is defined using the = operator, so there is no redunduncy. OTOH in the proposal made in this issue, the binding defined in the native code should match that defined in the JSX side. And as I have pointed out in my previous comment (please see https://github.com/jsx/JSX/issues/177#issuecomment-18602296), my assumption is that it would be difficult to define such a matching protocol.

nyuichi commented 11 years ago

@kazuho

  1. Native class is interface. It should not contain any implementations.

No. It is a binding. In some cases it is possible to define a binding without writing native code at all (an example of such case would be the built-in Object type), but in other cases you need to define both the interface for JSX and the binding in native code.

What is the difference between binding and interface? I meant that native class is a interface part to communicate with JS, not necessarily meant that it should be a interface class in JSX which does not contain any concrete methods and variables. And I know that there are some methods defined in native classes such as some of overridden method of forEach. But they are just convenient utilities or aliases. I don't think they are implementation details.

it would actually be easier to read / write / maintain if such bindings could be defined inline than enforcing them to be written in a separate file.

I disagreed. Such code won't be able to get any check and even highlighted. Allowing inline definitions directly means that even trivial errors like missing closing parenthesis are left ignored until the situation gets worse at runtime.

  1. It's even easier to introduce foreign import than #169. Change to the parser will be only one, and the only thing emitter has to do will be read contents from stub files to write them out in the head of the output code.

I agree that it would be easier for us (the developer of the JSX compiler). But it would not be easier for the users. In the proposal I made in #169, a native binding is defined using the = operator, so there is no redunduncy.

I believe that being easier for the developers is being easier for the users as well. You said that there's no redundancy in #169 but how about the timing of the code defined inline being executed? I'm afraid that the number of rules implicitly defined by the compiler would be increasing as the implementation work continues.

OTOH in the proposal made in this issue, the binding defined in the native code should match that defined in the JSX side. And as I have pointed out in my previous comment (please see #177), my assumption is that it would be difficult to define such a matching protocol.

I can't get your point. Is there any problem in letting users set their own protocols as needed?

nyuichi commented 11 years ago

We might better change the syntax, like

foreign "js" import "foo.js";
foreign "c++" import "foo.so";

to express file formats explicitly (and make it independent from the extension convention of the runtime environment).

kazuho commented 11 years ago

@wasabiz

it would actually be easier to read / write / maintain if such bindings could be defined inline than enforcing them to be written in a separate file.

I disagreed. Such code won't be able to get any check and even highlighted. Allowing inline definitions directly means that even trivial errors like missing closing parenthesis are left ignored until the situation gets worse at runtime.

Please do not quote out of context. I said: "in cases where the definition of the binding could be short (e.g. common.js), ...", and I do not see why you would prefer not to define bindings to common.js class inline.

native class Foo {
} = "require('foo.js').Foo";

is more intuitive and easy to maintain than using a separate JavaScript source file. It is true that you cannot get help of code highlighting or lint for the native expression, but I do not see that as an issue considering how short the expressions are.

Please do not get me wrong. I have not argued against allowing users to define native bindings in a separate file. I am only opposing to the idea of enforcing the users to do so.

I believe that being easier for the developers is being easier for the users as well. You said that there's no redundancy in #169 but how about the timing of the code defined inline being executed? I'm afraid that the number of rules implicitly defined by the compiler would be increasing as the implementation work continues.

OTOH in the proposal made in this issue, the binding defined in the native code should match that defined in the JSX side. And as I have pointed out in my previous comment (please see #177), my assumption is that it would be difficult to define such a matching protocol.

I can't get your point. Is there any problem in letting users set their own protocols as needed?

Consider a case where there are two native bindings that both export classes named C. We need to define a rule (protocol) to distinguish the two, implement it in the compiler, and the users would need to learn the rule. I do not think it is a easy task.

Anyways, I think we should look at the real-world use cases before deciding how we should proceed.

nyuichi commented 11 years ago

@kazuho

Please do not get me wrong. I have not argued against allowing users to define native bindings in a separate file. I am only opposing to the idea of enforcing the users to do so.

What I think is the problem is the fact that two of introduction of the essential functionality of JS interoperation and introduction of the short forms for that functionality are discussed at the same time. Only introducing #177 is sufficient at least for now, and if you think the short form is still needed for practical use afterwards then we can discuss it again. Anyway #169 requires a radical change to the language design which cannot be easily fixed after it is released. That should be considered more carefully and deeply.

Consider a case where there are two native bindings that both export classes named C. We need to define a rule (protocol) to distinguish the two, implement it in the compiler, and the users would need to learn the rule. I do not think it is a easy task.

In that case (but I'm not sure I really got your point,) users should write code that makes an alias to one of Cs, like export.MyC = C; or something like that in the stub file.