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)
Here's another advantage of my revision.
Since the Javascript references to Elm things would all be via the @{} syntax, and since the compiler would have to parse the Javascript, there actually would be some opportunities for the compiler to type-check the Javascript itself. For instance, in the revised
nonExposedFunction
, the compiler, when parsing the Javascript, would know the type ofy
. It would also know the type signature forNative.Utils.field
. So, in principle, a certain amount of type-checking would be possible.Now, I'm not saying that it would be complete -- it would probably not be. And, some type errors detected in the Javascript code might have to be warnings instead of errors, since it might be part of the point of the native code to do some things that the compiler can't verify in terms of types. However, in principle it is potentially an advantage if the compiler can make some explicit connections between the Javascript code and the Elm types.