Advanced Types in Elm - The Never Type

Part III: The Never Type

Charlie Koster
Sep 6, 2017 · 5 min read

Ok, ok. I’m bending the definition of what “advanced type” means with this post, especially since we’re reviewing a type found in the Basics module. Regardless, its usage is “advanced” as someone coming from a Javascript background since Javascript doesn’t have the concept of a Never type.

What is the Never type?

The Never type is a type that doesn’t have any values. It’s a type that can be specified in a type annotation, but you can’t construct a Never value because it is valueless.

If you are coming from a language like Javascript this is a bit strange. It’s a type that doesn’t have any values? Let’s reiterate a point made by the docs.

  • A Bool has two values: True and False
  • The unit type has one value: ()
  • The Never type has no values

How can this be? This starts to make more sense if we look at how Never is defined in the source.

Image for post
Image for post

Let’s try and figure out how to use Never as a value. We’d need to use JustOneMore and pass it some additional information.

Image for post
Image for post

Ok we need to fill in the blank. JustOneMore expects a Never. Easy.

Image for post
Image for post

Hmm. Never is recursively defined. This might take a while..

Hopefully it’s a little more clear that because Never is recursively defined it is impossible to use it as a value. Additionally, Never is an opaque type with no constructor functions exposed. It couldn’t be constructed outside of the Basics module even if it wasn’t recursively defined! There’s more info on opaque types in a previous post.

Yea, I was too when I first started doing research for this post. How can there be an annotated type without a corresponding value?

The answer is simple. Not all types in a type annotation require a corresponding value in the function implementation.

Image for post
Image for post

The code above demonstrates that we see this sort of thing every day. In that example alwaysNothing is annotated as returning a Maybe String, yet the body of the function doesn’t contain a string value. There’s an annotated type without a corresponding value.

Likewise, I could also update the annotation to return a Maybe Never.

Image for post
Image for post

This function compiles just fine. The really neat part is if I change Nothing to Just "any type here" then I get a compiler error because Never doesn’t have any values. It forces this function to actually always return Nothing by leveraging the type system!

Note that the docs for Never assert that “generally speaking, you do not want Never in your return types” so take this example with a grain of salt.

It seems obvious in hindsight, but honestly a lot of this didn’t click for me until I realized that Never will likely be used in conjunction with parameterized types like Task or Html.

When is it useful?

As is the case with extensible records, the Never type is useful for restricting function arguments.

The Task module does a good job of demonstrating this. There are two functions in that module that produce a Cmd msg. The first function is attempt.

Image for post
Image for post

This function takes two pieces of information and returns a Cmd. The two arguments include:

  • A task that might fail
  • A function that takes the Result from executing the task and turns it into a msg

This is all reasonable. But what about tasks that can never fail? For example, Time.now can’t fail. If a task cannot fail then we don’t need to handle the error case. In fact, there shouldn’t be any Result to handle at all!

Well, that’s what perform does.

Image for post
Image for post

The perform function operates on tasks that can never fail. Because a Task can be annotated with Never (the type of the error is Never) this function is guaranteed to never be called with a task that can fail. And since the task can never fail the first argument to the function can shrink from (Result x a -> msg) to (a -> msg). Never is restricting the function arguments.

If you’d like to play around with how the Task module uses Never I put together a small example with attempt, perform, a task that can fail, and a task that will never fail.

Note that you can replace the usage of taskCanFail with taskWillNotFail, but not the other way around.

Conclusion

The Never type is a valueless type and like extensible records the Never type can be used to restrict function arguments, and that’s all there is to it!

Next up.. a look into phantom types..

Shoutout to the folks on the #beginners slack channel for answering my questions and adding clarification in a friendly way. You’re all incredibly helpful!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store