There’s a neat zero-cost trick in Haskell for types with one constructor and a single field inside it. Instead of using
newtype and the typechecker will treat your type differently, but the representation of your data at runtime will be the same. This lets us get better typechecking in some cases, without changing the runtime performance of programs.
A sort of classic (if boring) example of this is to create newtypes for different fields describing a person. Instead of
We can instead use something like
Then when we’re trying to call a function
getLatLng :: Address -> (Double, Double) the typechecker stops us from accidentally passing in a
FirstName. (Even better, we keep
LastName as distinct types, which helps keep us from ever getting confused about which is which—the compiler will check for us automatically.)
There’s a function called
coerce :: Coercible a b => a -> b which lets us convert between various types that have representational equality (that is, they have the exact same structure at runtime). This could be useful if your code needs to call some external library that takes first names as
Text, but the library doesn’t know about your
FirstName type. You just coerce the
Text and everything works, with type safety guaranteed by the compiler.1
What kinds of things are coercible? You shouldn’t go around writing your own arbitrary
Coercible instances—that’d likely break type safety. How does the compiler know whether two things are coercible then?
There are some obvious cases. First of all, any type is coercible to itself (it has the same runtime representation as itself). Also, like above, if we have a newtype we can coerce to the underlying type of that newtype. In this case we’re coercing
Text. We can also go in the other direction, from
It’s also fairly obvious that we can inductively build up complicated coercibles if all the parts are coercible (think about some nested structure containing only
Coercibles—this tree has the same runtime representation as any other tree of the same representational structure).
I won’t go much into it here,2 but if you know about roles then it’s pretty easy to determine what the typechecker is doing when determining whether two types are coercible. Types with the
phantom role essentially don’t matter. This makes sense because phantom types don’t affect the underlying runtime representation at all. Types with the
nominal role can’t be coerced. Again, makes sense because
nominal enforces strict sameness. And types with a role of
representational can be used in a
Coercible if they are themselves coercible. Makes sense: the
representational role means that the underlying representation is the same.
Identity functor is intimately connected with coercibility, because it’s just a simple newtype around any other type.
Identity is useful for many of the same reasons that
id is—it slots into a functor-shaped hole, but doesn’t do too much.
The instinctive way to write a functor instance for
Identity is to define
fmap as unwrapping with
runIdentity, then applying
f, then re-wrapping with the
However, now that we know about coerce we can simplify the above a bit.
(#.) operator could be called the “
Coercible composition operator”. It takes two coercions (from a to b, and from b to c) and produces a single composed coercion (from a to c). We can define
#. like this:
If you strip away all the type machinery, we’re left with just
coerce (\x -> x), which reads an awful lot like coercing the identity function to behave like a coercing identity function.
With the type machinery, though, we see that we’re building a function that requires a to be coercible to b (for all a and b), and also asserts that x has type b. It sort of “skips” the initial coercion step from a to b, because as long as the type
Coercible b a is in there, we can do just one coercion. This is all just a lot of compile-time checks for one single “composed” coercion, but unlike a usual
(.) composition, we don’t have things to do at runtime so it looks like the function basically does nothing! It even ignores its first argument, which would be silly for usual composition to do. Just try writing an implementation for
_ . g…you really need
f . g if you want to get anything done.
One place that
#. is used is in the implementation of lenses. Consider this implementation of
set, adapted from Building Lenses.
The implementation is a little weird so let’s walk through it. First of all, we have two things to keep track of, a
setter and some value of the final transformed-to type
b. What a setter does is replace some internal value in a structure with a new value. The type of a
Setter looks like this (don’t worry about what
Settable means for now):
Let’s try to read the
set function from the inside out. First of all, we don’t really care about the value of type
a since it’s getting replaced by a
b anyways. That explains the underscore in
(\_ -> Identity b). We wrap
Identity because we need some functor
f and what could be simpler than using the
Identity functor? We also pass
s right in (the pointfree third argument to
set), so the right half (
setter (\_ -> Identity b) s) should make some sense now.
We have a
Setter built, but we still need to get a
t from it to satisfy the return type of
set. Remember that
Identity, so going from
f t to
t should be as simple as calling
runIdentity. Finally, we use
#. instead of plain
. composition because we shouldn’t care about typechecking newtypes differently here, only that the final representations end up the same.