-- src/Main.elm
module Main exposing (answer, sum)
answer : Int
answer = 42
sum : { a: Float, b: Float} -> Float
sum data =
data.a + data.b
and:
elm2node src/Main.elm
The tool still is in alpha and only available for linux for now. I’m seeking for people helping me compile it for the other platforms and of course feedback!
Whoops! This is neat. I see a use case for writing isomorphic code (meteor-like) for data that needs validation on the server or should be hidden from the user but is tightly integrated with the data structures prevalent in your client-side code.
Publish this as JS lib on npm would be great but I’m afraid it suffers some serious performance hit. All the parameters are encoded with a Json.parse(Json.stringify(parameter)) which is the way elm deals with parameter coming from JS; in the context of UI this is not a big since most of the time is spent on redrawing.
But for lib called server side, it might take some time.
A solution would be to emit typescript annotation and get rid of the Json.parse(Json.stringify(...)). (up to the user to use TS instead of JS).
prevent some weird edge cases like JS objects making side effect at properties read.
Thinking about this, if we want 1. to be still true, we can not prevent the copy of arg.
That said, I think both methods are really fast and optimized (hey, they are used everywhere in JS), maybe it is the fatest way to have 1. and 2. hold.
Neither of these lines are invoked when passing a value through a port. The first is called only when trying to parse a String, like from the body of an HTTP response. There is also Json.Decode.decodeValue which deals only with Values. The second line similarly is only invoked if you need to render a Value to a String by explicitly calling Json.Encode.encode.
When the compiler generates code for ports it checks the type and sets up the appropriate converter from JS to Elm for that type. Lists and tuples, for example, get special treatment by getting converted to and from JS Arrays, but in the case of a Value, the converter is identity or Json.Decode.value (the Decoder analogue to identity). No stringifying.
Generated port code for a Value looks like this:
var $author$project$Module$portName = _Platform_outgoingPort('portName', $elm$core$Basics$identity);
var $author$project$Module$portName = _Platform_incomingPort('portName', $elm$json$Json$Decode$value);
I don’t know why I thought those functions was used during port process. I’ve checked what I’ve done and I don’t use these functions neither.
So my claim was wrong.
In the case we would want to use typescript to get rid of the encoding part, I wonder if we should care about “copy” the function argument in order to prevent mutation by the caller. If we do:
arg = { b: 5};
res = elmModule.f(arg);
arg.b = 4;
this should not bother f . But is it possible for arg to be modified in another place (say it is a global variable), during the execution of f?
It would not be possible for another piece of code to modify arg during the execution of elmModule.f since JavaScript is single-threaded. However, if elm code keeps a reference to the json Value, it is possible for two decodings of the value to produce different results if some non-elm code runs in between.
In a normal elm program, this is a possibility (and is actually sometimes useful), but I don’t know enough about your code to know whether it is a possibility in your library. Does your library ever keep any state, or in other words does your library allow wrapping Program types? If not, then it should be safe.
There are no way to guarantee that a given value x returned by an “elm-converted-to-js” function cannot be modified by JS code. So I’ll never try do allow things like:
// js file:
var someOpaqueElmValue = elmModule.f(42);
var result = elmModule.g(someOpaqueElmValue);