Something to watch for while using record updating syntax
Let’s pop open GHCi and construct a record with a type tag.
We’ll want to be able to add some extra type annotations (thing :: type
) in places we can’t normally, so let’s turn on ScopedTypeVariables
:set -XScopedTypeVariables
We want to make a type X
with a tag t
. t
lets us differentiate between X
s by keeping information in the type, but without having to store any additional values. X
also has a record with some stuff inside it, but the kind of stuff in X
isn’t super relevant.
Now that we have an X
type, let’s make a value of type X
.
Double-check the type of myX
. Looks like it’s still hanging on to the Int
tag.
However, what happens when we try to update the record? Looks like Int
has gone away, leading to the much-less-informative polymorphic t
.
We lost our tag due to the record update! This might be confusing in real code, since we can do things like assign what was just previously an X Int
to an X String
. Because the record update doesn’t keep the tag around, the tag’s usefulness diminishes completely in this case.
To understand why this happens, let’s write a similar updater function ourselves.
replaceC
is a function that takes an X
and replaces the c
inside. It acts just like a record update.
Also just like a record update, replaceC
doesn’t ensure that the tag sticks around.
Looking at its type, we can see why:
The most general type for replaceC
allows t1
and t2
to be different (and GHC loves to be as general as it can). To say that they should be the same is an additional restriction. However, in the case of keeping tags around, it’s really nice not to lose the tag just because you updated some other part of the data structure.
By adding a type signature specifying the restriction that replaceC
should preserve t
, we get to keep tags even when we replace bits of the record.
However, I wasn’t able to find a great way to do this tag preservation while still using the record update syntax (which is essentially just some syntactic sugar over a function kind of like replaceC
). Hopefully someone knows a good trick for this.