Right now, Native doesn't play well with dead-code elmination. Additionally, Native review is a big bottleneck for producing and publishing new librairies. Finally, there is no way to unclude JS code which calls ports using the Elm compiler.
/**
--The top of the file declares the structure of the Native module
--and must be a valid Elm syntax declaration
module Native.SomeModule where
--Used Native modules must be explicitly imported
import Native.Utils
--We can import pre-existing library functions
--this includes them in the final output
--but they can't be explicitly called from Elm.
--A Native module must act as an interface.
import raw "Bootstrap.js"
-every used non-Native function must be explicitly imported
import Signal exposing (constant)
--Each exposed Native function is declared as a String literal in Elm
--with the name of the JS function that defines it
--This lets us statically check that no top-level functions are missing
nativeFun = "nativeFun"
otherFun = "otherFun"
**/
//The rest of the JS file is just a series of JS statements
var nativeFun = function(x)
{
if (Elm.Utils.constructor(x) == "Nothing" )
{
Elm.Utils.construct("Nothing");
}
//We construct "type" (algebraic data) values using a library function
return Elm.Utils.construct("Just", 3);
};
var otherFun = function(y)
{
return nonExposedFunction(y);
};
//We are allowed to declare other functions
//that can't be imported by Elm
var nonExposedFunction(y)
{
var ret = [];
//We build records out of arrays
//and access fields using a library function
ret["field"] = Elm.Utils.field(y, "elmField");
ret["field2] = 3;
//a library function constructs the record out of the array
return Elm.Signal.constant(Elm.Utils.record(ret));
};
- The structure of the module is declared at the top, using normal Elm syntax
- The user doesn't write a .make() function, or return a .values object. That's all done mechanically, we would be guaranteed those functions/values would always be well formed.
- The imported Native modules, and imported and exported Elm functions, must be explicitly declared
- Elm ADT and Record values are not manipulated directly, but using a library function, so Native modules would not break if Elm changed its internal representations.
- There's new syntax for including a JS file in compilation, such as pre-existing libraries.
- The module structure is defined using Elm syntax, so we can use existing parsers, and it flows nicely with the rest of the project.
- Much less Native review needed to ensure a module is well formed.
- More resistant to breaking changes.
- The
import raw
syntax can be used to include the JS side of Ports code.
- Could be slower, since we're calling library functions to access Elm values. (Could be solved by inlining).
- Requires pre-processing of comments (could be solved by putting the structure Elm declaration in its own file).
- There are now 3 types of JS ffi (Native, Ports, import raw)
Including Ports code
I believe that there is no reason to include the JS side of Ports code now that tasks exist. Anything that needs to react on an outgoing signal can be written as a Native JS Task. For example, see my pull request to remove the JS Ports handling code from elm-io:
https://github.com/maxsnew/IO/pull/18/files
Explicit import of members from raw JS
I believe you should only be allowed to import other JS files with:
import raw "Backbone.js" exposing (Backbone)
. This will generate code which wraps the included JS file in a local scope, and only returns scope.Backbone:This solves two problems: