Lots of things are very nice in Elm but occasionally you find a gap. For me, currency formatting was such a gap. It’s non-trivial enough to be annoying if you need to deal with different locales and different currencies. Javascript has a perfect api for this in the form of the toLocaleString function.

Elm has an undocumented feature called Native modules allowing you to call javascript functions directly. Of course this is a bad idea and can easily re-introduce all of the type chaos and runtime errors that you were trying to escape. But in a case like this it seems reasonable — why reinvent a perfectly good api?

The information I was able to find about how to do this was a little out of date and that’s why I’m writing up how it can be done in Elm 0.18.

Create a normal Elm module

First step is to create a normal Elm module to expose your function.

module Utils.CurrencyFormat exposing (format) --this is where we import the native module

import Native.CurrencyFormat format : String -> String -> number -> String

format culture currency num =

let

res =

Native.CurrencyFormat.format culture currency num

in

case res of

Ok str ->

str Err err ->

let

_ =

Debug.log "CurrencyFormatError" err

in

toString num

Create your native js file

Next create a js file called /src/Native/CurrencyFormat.js. Now, once again, this is not safe so do everything you can to make this js function bullet-proof.

var _user$project$Native_CurrencyFormat = (function () { var format = function(cultureCode, currency, num) {

try {

var result = num.toLocaleString(cultureCode, {style: 'currency', currency: currency});

return {

ctor: "Ok",

_0: result

};

} catch (e) {

return {

ctor: "Err",

_0: e.message

};

}

};

return {

format: F3(format)

};

})();

This will expose this function to Elm to be called from the module we created in step one. Notice a couple of things: toLocaleString can fail if we give it bad inputs. For this reason we wrap the call in try catch and we return the result as an Elm (Result String String) by supplying the constructor of either Ok or Err. We could choose to pass that result on to the calling code or handle it in our utility function as I have done by just logging it and returning a plain stringified number. It depends how important the error is to you.

The second thing to note is that I am wrapping the returned function in a call to a mystery F3 function. I am guessing a little bit here, but I’m pretty sure this ensures that the function is appropriately curried so that partial application will work as expected on the Elm side.

Configure your app to allow native modules

Make the following small change to your elm-package.json file



"version": "1.0.0",

"summary": "",

"repository": "

"license": "BSD3",

"source-directories": [

"./client/elm"

],

"native-modules": true,

"exposed-modules": [],

"dependencies": {

"elm-lang/core": "5.0.0 <= v < 6.0.0",

"elm-lang/html": "2.0.0 <= v < 3.0.0",

"elm-lang/http": "1.0.0 <= v < 2.0.0"

},

"elm-version": "0.18.0 <= v < 0.19.0"

} "version": "1.0.0","summary": "","repository": " https://github.com/user/project.git ","license": "BSD3","source-directories": ["./client/elm"],"exposed-modules": [],"dependencies": {"elm-lang/core": "5.0.0 <= v < 6.0.0","elm-lang/html": "2.0.0 <= v < 3.0.0","elm-lang/http": "1.0.0 <= v < 2.0.0"},"elm-version": "0.18.0 <= v < 0.19.0"

Consume your function as normal

Your function should work just like a normal Elm function if you’ve done everything right. It will even support automatic currying like any other Elm function. Try it out …

toCurrency : number -> String

toCurrency =

Currency.format "en" "GBP"

I think that in some limited circumstances this is a good solution. But beware, this is an undocumented feature and it can change and be broken at any time so use it at your own risk.