{"action":"created","issue":{"url":"https://api.github.com/repos/nim-lang/Nim/issues/4873","repository_url":"https://api.github.com/repos/nim-lang/Nim","labels_url":"https://api.github.com/repos/nim-lang/Nim/issues/4873/labels{/name}","comments_url":"https://api.github.com/repos/nim-lang/Nim/issues/4873/comments","events_url":"https://api.github.com/repos/nim-lang/Nim/issues/4873/events","html_url":"https://github.com/nim-lang/Nim/issues/4873","id":181902034,"number":4873,"title":"RFC: improving JavaScript FFI","user":{"login":"andreaferretti","id":1962818,"avatar_url":"https://avatars.githubusercontent.com/u/1962818?v=3","gravatar_id":"","url":"https://api.github.com/users/andreaferretti","html_url":"https://github.com/andreaferretti","followers_url":"https://api.github.com/users/andreaferretti/followers","following_url":"https://api.github.com/users/andreaferretti/following{/other_user}","gists_url":"https://api.github.com/users/andreaferretti/gists{/gist_id}","starred_url":"https://api.github.com/users/andreaferretti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andreaferretti/subscriptions","organizations_url":"https://api.github.com/users/andreaferretti/orgs","repos_url":"https://api.github.com/users/andreaferretti/repos","events_url":"https://api.github.com/users/andreaferretti/events{/privacy}","received_events_url":"https://api.github.com/users/andreaferretti/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/nim-lang/Nim/labels/RFC","name":"RFC","color":"1d1d1d"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":3,"created_at":"2016-10-09T18:20:48Z","updated_at":"2016-10-10T12:49:24Z","closed_at":null,"body":"I have had some experience in compiling Nim to Js while writing [bindings for React](https://github.com/andreaferretti/react.nim). After this experience, and taking inspiration from Scala.js, I think there is the opportunity to streamline the FFI a little. Everything I suggest in the following should be doable in the stdlib, with no changes to the compiler. If there is interest in the proposal, I can try to help with the task.\r\n\r\nI suggest the introduction of a module `jsffi` that contains some types and macros useful while interfacing with JavaScript. Here are the use cases:\r\n\r\n1) When interfacing with existing js libraries, one often needs to pass some functions an object containing options. Usually, these objects have many fields, and the user only sets the relevant ones. An example may be the `settings` in [jquery.ajax](http://api.jquery.com/jquery.ajax/) function, the [attributes in React components](https://facebook.github.io/react/docs/tags-and-attributes.html) and so on.\r\n\r\nOne can create custom types for these options, but usually these end up with many fields. All these fields need to be declared with `{.exportc.}` to avoid name mangling. As seen in [this example](https://github.com/andreaferretti/react.nim/blob/master/react.nim#L28), this get rapidly out of control. I think one could create some macro to automate all the `exportc` declarations.\r\n\r\n2) If one creates instances of such types with only a few parameters, the remaining ones end up populated with `null`. This is an issue for two reasons:\r\n\r\n* objects are much bigger than needed, but more important\r\n* `null` may be an actual valid value for such objects.\r\n\r\nThe `lit` macro [suggested by @mjendrusch in this issue](https://github.com/nim-lang/Nim/issues/4859) (and started with help from @oderwat in the forums) could be included in the module to help with such cases. It allows users to write\r\n\r\n```nim\r\ntype Foo = ref object\r\n  a {.exportc.}, b {.exportc.}: cint\r\n  c {.exportc.}: cstring\r\n\r\nlet x = Foo.lit(b = 1, c = \"hello\")\r\n```\r\n\r\n3) The manual [suggests](http://nim-lang.org/docs/manual.html#special-operators-operator) that the `.`, `.()` and `.=` operators can be overriden to interface with dynamic languages.\r\n\r\nIn the spirit of Scala [Dynamic](http://www.scala-lang.org/api/current/index.html#scala.Dynamic) I suggest to have a standard type `JsObject` in `jsffi` where such operators are in fact overriden with the corresponding member access in JS, to make [quick and dirty](https://www.scala-js.org/doc/interoperability/types.html#dynamically-typed-interface-jsdynamic) interfacing easier\r\n\r\n4) A macro `jsObj` similar to the one in point 2 could generate literal instances of javascript objects having type `JsObject`. From the point of view of the user, generating instances of `JsObject` would simply be\r\n\r\n```nim\r\nlet x: JsObject = jsObj(\r\n  a = 1,\r\n  b = \"hello\",\r\n  c = true\r\n)\r\n```\r\n\r\nIn fact, the macro in point 2 could become the macro `jsObj` plus a cast to the correct type.\r\n\r\n5) Sometimes, one needs to pass objects with methods to an external API. Such methods may need to refer to the `this` object, which in javascript is dynamically bound. An example is writing React components, where one needs to provide methods such as `componentDidMount` - such methods will need to refer to the `this` object to access `this.props` and `this.state`.\r\n\r\nThe trick, shown to me by Oderwat, is to use\r\n\r\n```nim\r\nvar this {.importc,nodecl.}: C\r\n```\r\n\r\ninside the method body, where `C` is the type of `this`. Using this I have been able to write [a macro](https://github.com/andreaferretti/react.nim/blob/master/react.nim#L203) that allows users to write `componentDidMount` and such as normal procs, where `this` is passed as the first argument.\r\n\r\nI suggest that a similar macro, more general than the one I use here, should be added in `jsffi` to make it easy to attach javascript methods to objects."},"comment":{"url":"https://api.github.com/repos/nim-lang/Nim/issues/comments/252609286","html_url":"https://github.com/nim-lang/Nim/issues/4873#issuecomment-252609286","issue_url":"https://api.github.com/repos/nim-lang/Nim/issues/4873","id":252609286,"user":{"login":"andreaferretti","id":1962818,"avatar_url":"https://avatars.githubusercontent.com/u/1962818?v=3","gravatar_id":"","url":"https://api.github.com/users/andreaferretti","html_url":"https://github.com/andreaferretti","followers_url":"https://api.github.com/users/andreaferretti/followers","following_url":"https://api.github.com/users/andreaferretti/following{/other_user}","gists_url":"https://api.github.com/users/andreaferretti/gists{/gist_id}","starred_url":"https://api.github.com/users/andreaferretti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andreaferretti/subscriptions","organizations_url":"https://api.github.com/users/andreaferretti/orgs","repos_url":"https://api.github.com/users/andreaferretti/repos","events_url":"https://api.github.com/users/andreaferretti/events{/privacy}","received_events_url":"https://api.github.com/users/andreaferretti/received_events","type":"User","site_admin":false},"created_at":"2016-10-10T12:49:24Z","updated_at":"2016-10-10T12:49:24Z","body":"Yeah, I think such patterns should be centralized because they are useful for everybody. I will try to put down some preliminary example and share what I come up with. Thank you for the link!"}}
{"actor":{"display_login":"andreaferretti"}}