Newtype coercion

Contents
  • The Coercible class
  • Newtype wrapping/unwrapping
  • Newtypes for anything
  • Some types seem strange
  • Coercibility is transitive
  • Coercion via type parameters
  • Coercions of functions
  • Deriving via
  • Monad transformers
  • Constructor visibility
  • Type roles
  • Phantom type parameters
  • Nominal type parameters
  • History

The newtype keyword allows us to define a new type that has the same runtime representation as another type.

Consider this in contrast with a type alias, which merely creates a new name to refer to the same type. This doesn’t provide any additional type safety, as illustrated by the mistake in the following example:

A newtype definition actually creates a different type, so when we make the same mistake again in the follow example, the typechecker refuses to allow the erroneous code to compile:

This is good – The compiler caught our mistake. But there is a downside! Sometimes we may want to be able to treat Names like Strings and vice versa. In the type alias version, we could write this:

But in the newtyped version, abbreviateName won’t typecheck, because Name and String are not the same type.

To make the abbreviateName code above compile, we have to introduce coercions in two places:

  1. Use the Name constructor as a pattern in the function’s argument to coerce the argument from Name to String.
  2. Use the Name constructor as an expression in the function body to coerce the abbreviated String to a Name.

This can become more cumbersome as the code grows more complicated or as we add more layers of newtypes. Fortunately, GHC gives us some tools that help us express these trivial type conversions more easily.

The Coercible class

GHC provides a function called coerce that helps us perform these trivial conversions between types. It can convert between two types A and B as long as the constraint Coercible A B is satisfied.

Although Coercible Data.Coerce in the base package is a magic feature of GHC, you can safely understand it as a typeclass defined as follows: You do not need to enable any language extensions to use the coerce function or to use the Coercible class in constraints. Coercible is a multi-parameter typeclass (it has two type parameters), but the MultiParamTypeClasses extension is only required to define multi-parameter typeclasses, not to use them.

You cannot define your own instances of Coercible, but you get a lot of instances for free.

Newtype wrapping/unwrapping

The most basic circumstance in which Coercible instances arise is whenever a newtype is defined.

If we have a definition that looks like this:

Then we automatically receive the following two Coercible instances:

Sign up for access to the full page, plus the complete archive and all the latest content.