5 Things you didn’t know about Elm
Or perhaps you did. Reminders can’t hurt, though.
1. Changing types in record update syntax
Record update syntax { a | b = c }
is restricted in various ways: you cannot add or remove fields, but only change the values of fields. In that regard, they are very much like labeled tuples: the general shape can’t change. However, here’s something you can do that may come as a bit of a surprise: changing datatypes.
Naturally, if you have a type alias
that says some field needs to be a certain type, and you change that type somewhere, the resulting record will no longer conform to that type alias. Here are a few examples illustrating that.
When is this useful?
As an example, imagine we use some browser detection library in JS like platform.js which can potentially result in null
for every value. Let’s say we’re also taking a seed for a random number (because we don’t trust using the current time). We can do that as follows:
2. Ports and flags don’t require automatic conversion
Both ports and flags do automatic conversion of raw JS values to Elm types; but that comes with limitations, for example custom union types.
Json.Encode.Value
to the rescue! A Value
type encapsulates a raw JS value; so annotating your ports or init
function to accept a Value
basically means that Elm won’t do its automatic conversion for those values. You will, however, need to handle the decoding and conversion yourself.
The same goes for outgoing ports, where you can handle the encoding yourself before passing the value of to the port.
Automatic conversion is sort of a relic from times when there wasn’t a streamlined Json.Decode
library, and it was basically the only way to handle foreign values.
When is this useful?
- Decoupling your Elm types from your JS types
- Handling union types
- Control over what happened when you receive bad values — rather than an error, you get to graciously handle it in Elm.
3. Unsubscribe from outgoing ports
Let’s say you have an outgoing port in your Elm app and subscribed to it from the JS side. How does one do that?
You unsubscribe
of course! Note that you need to pass the same callback to unsubscribe
as you originally passed into subscribe
— similar to working with event handlers. There’s even some magic so you can call unsubscribe
from within your subscription handle, meaning you can pass this
and unsubscribe without clutter!
As illustration, have a look at the following Ellie. After 5 clicks, they will no longer be printed to the console.
4. Pipelines are free!
Or, more correctly — the right and left function application operators |>
and <|
are special cased in the compiler so they don’t cause any overhead.
Thanks to Richard Feldman for this little gem.
Naively, a |> b
would compile into a function calling the |>
operator with a
and b
as arguments; after which the |>
operator would call b a
. Under that assumption, simply writing it as b a
instead would be more performant. However, that’s not actually what happens — the compiler knows about <|
and |>
and gives the special treatment; basically inlining their operation.
This means that pipelines, which improve readability, don’t come at some sneaky cost; they’re free!
5. Phantom types are a thing
Phantom types are types in which type variables are declared on the right-hand side that aren’t used in the left-hand side. As an example type MyType a = My String
where it’s clear that a
isn’t used. This somewhat weird construct gives us the ability to express certain constrains about the values without directly affecting the values.
Thanks to Martin Janiczek for brining this up in the Elm-lang slack earlier!
For example, it becomes possible to create a FormData
type that is Unsanitized
by default, but allows sanitizing the input using a user-provided Sanitizer
and restricts submit
ing to Sanitized
data, like follows.
Now, on its own, this isn’t particularly useful — the same could be accomplished by putting the data in Unsanitized
and Sanitized
and throwing away the whole FormData
type. However, this becomes useful when dealing with functions that don’t care about whether or not the data is sanitized, but just need a FormData a
and can work on that type. In order to do that with two distinct types, you’d have to write different functions and give them different names, and double the maintenance effort.
Googling around for phantom types will probably offer you a whole host of other use cases than this one, definitely worth checking out.
So there you have it, some neat things you might not have known about Elm. If you enjoyed this post, feel free to check out my other posts and don’t hesitate to click that green little heart!