Since Elm compiles to JavaScript, every function you use in Elm should have a translation to a normal JavaScript function that will be executed in the browser. Those basic building blocks that make up Elm are defined in the Native
modules of the elm-lang core.
While I was working on the Chrome app for the Mooltipass, I needed to change one of those native JavaScript functions on which Elm is based. We are using the dropDown
function to create a dropdown HTML element, but the problem was that there was no way to say which element had been selected. So we needed to add an extra argument to the function that would be used to indicate the selected element.
Conrad Parker solved this a while ago. At that time the source code contained a copy of the elm-lang core libraries because of a bug that was present in one of the older version of the core libraries. So his fix was made in the core libraries as well.
Because I upgraded the core libraries to the latest version, I stumbled unto this bug again. So I wanted to fix it again, but in a way that would not break if we update the core libraries again.
To do this I first created my own dropDown
function which just calls some custom JavaScript function.
dropDown : (a -> Signal.Message) -> List (String, a) -> String -> Element
dropDown = Native.CustomGraphics.dropDown
The custom JavaScript function is just the function that was written by Conrad. I extracted the part about the dropDown from Native.Graphics.Input
in the core libraries, and put this my own Native/CustomGraphics.js
file. This just defines a make
function that returns all functions that can be used, which in this case is just the dropDown
function.
To be able to use this in our Elm app, we still need to be able to tell Elm where it can reach this function. So the first thing we need to do is load the native JavaScript file in the HTML page that loads the Elm code.
<script type="text/javascript" src="Native/CustomGraphics.js"></script>
But this is not enough. When Elm compiles the .elm
file where we use the dropDown
function, it needs to have access to the native module we just defined. Normally when you create an Elm module, Elm makes sure the right native files are accesible in the compiled JavaScript code. It does this by calling the make
file we just created and savind the result in a variable. When we look at the compiled code that uses the custom dropDown
function we defined earlier, it contains this line.
Elm.CustomGraphics.make = function (_elm) {
...
$moduleName = "CustomGraphics",
...
var dropDown = $Native$CustomGraphics.dropDown;
...
When we try to run this code, Elm will complain it doesn’t know this $Native$CustomGraphics
variable. In order to do this, I added a small step to the Makefile that builds the source code. After Elm is done compiling the source code, I run a regex expression on the file that adds the definition for $Native$CustomGraphics
.
sed -i.bak "s/\(\$$moduleName = \"CustomGraphics\",\)/\1 \$$Native\$$CustomGraphics = Elm.Native.CustomGraphics.make(_elm),/" elm.js
This changes the line
$moduleName = "CustomGraphics",
to
$moduleName = "CustomGraphics", $Native$CustomGraphics =
Elm.Native.CustomGraphics.make(_elm),
which is the make
function we defined earlier. Now we can run the code with our modified dropDown
function without changing the core libraries, hurray!
I haven’t figured out yet how Elm decides at compile time which JavaScript files need to be loaded in the resulting JavaScript file, or how it decides which specific make
functions need to be called in order to have the right native functions available. It would be interesting to try and hook into the compile process and tell the compiler where it needs to use our custom JavaScript module instead of hacking it in with a regex.