-
Notifications
You must be signed in to change notification settings - Fork 16
Native best practices
Native is dangerous. It can introduce runtime exceptions into your code. For that reason, we have a set of best practices when writing Native code.
You can bring things in from Native as Json.Value
. This is better than bringing them in without
TODO: expand with examples
Input types from Elm are compile-time checked, outputs from Native code is not. As a rule, always make sure that your function will return the same type for a given object.
The following function is fine, as the Elm code guarantees that the function will only receive an int.
var add = function(n){
return n + n;
};
add : Int -> Int
The following function is not
add : a -> a
It will work fine for Int
, String
, Float
, etc. But as soon as it's given any other type for which there is no +
defined in Javascript, it will crash.
-- this will work fine
four = add 2
-- this too
nana = add "na"
-- this will kill the type system at _runtime_
oops = add Nothing
The following function is not okay
var select = function(query) {
return document.querySelector(query);
};
select : String -> Node
The problem comes from the fact that document.querySelector
returns null when it fails to find anything. Elm won't complain, as the Elm code will wrap the null
with the type Node
. The problem will come when you then try to use a Node
in another function. You will get a null
error.
-- no error here
nullNode : Node
nullNode = select "doesntexist"
-- this will die, and introduce a runtime exception
-- causing the runtime to die
content = getAttribute "content" nullNode
Instead, check for null
and return a maybe.
var select = function(query) {
var node = document.querySelector(query);
if (node === null) return Maybe.Nothing;
return Maybe.Just(node);
};
select : String -> Maybe Node
See https://github.com/elm-lang/core/issues/453#issuecomment-161050745
There's no real way outside of Tasks
to signify when a function is impure in Elm. So, let's use Task
everywhere!
activeElement : Task x QueryNode
activeElement =
Native.Query.activeElement
Don't do
import Native.MyNative
import MyOther
do
import MyOther
import Native.MyNative
The reason here is that occasionally, in some cases, if you use the incorrect import order, it will cause Elm to crash at runtime (albeit in a vocal way). We're currently unable to figure out why this happens, but it happens in this case here