Deprecation of constructors keyword
Migrate your code to remove the obsolete
constructors
keyword
On November 20, 2018 the language standard changed
to remove the need for the constructors
keyword and beginning the deprecation cycle for the
constructors
keyword. This section describes what changed and how to migrate your code.
Changes
Before this change users would generate constructors for a union type using the constructors
keyword, which converted a union type into a record of constructors:
let Example = < Left : Natural | Right : Bool >
let example = constructors Example
in [ example.Left 1, example.Right True ]
After this change, you can now access union constructors as if they were fields of the
original union type instead of creating an intermediate record of constructors
. For
example, this is now valid:
let Example = < Left : Natural | Right : Bool >
in [ Example.Left 1, Example.Right True ]
Phases
The constructors
keyword is being phased out in three steps:
Phase 1 - Allow accessing constructors as fields of the union types
The first phase is backwards compatible, adding support for accessing constructors directly from a union type without changing the behavior of the
constructors
key word.Phase 2 -
constructors x = x
The second phase slightly breaks backwards compatibility by changing the constructors keyword to behaves as if it were the identity function for both type-checking and normalization purposes. In other words, any Dhall expression of the form:
constructors x
… behaves exactly as if it were just:
x
For the common idiom of:
let SomeUnionType = … let someUnionType = constructors SomeUnionType in … someUnionType.SomeConstructor …
… this is not a breaking change because
someUnionType
becomes a synonym forSomeUnionType
and you can access constructors directly off the original union type just as you did for the oldconstructors
record.This change also improves type-checking and normalization performance because the interpreter no longer needs to materialize the intermediate record of constructors. Now the interpreter only needs to type-check and normalize the constructors that you actually use.
However, this is still a breaking change because
constructors x
is now a type instead of a term. For example, this means that you can no longer store the expressionconstructors x
inside of a record containing other terms:-- This expression used to type-check before this change and -- no longer type-checks afterwards { foo = constructors MyUnionType , bar = 1 }
This change would also break any code that gave an explicit type annotation to a
constructors
expression (unlikely, but possible).Phase 3 - Remove the
constructors
keywordThis change is strongly backwards-incompatible by removing support for the
constructors
keyword, breaking all code that still uses the keyword.
Migration
Phase 1 - Manually migrate your code
During Phase 1 the
dhall
interpreter does not provide support for code migration since replacingconstructors x
withx
is not a safe transformation until Phase 2. If you wish to migrate your code you will need to do so manually.The main benefit of migrating your code manually (before Phase 2) is to get early access to the performance improvement from eliminating the intermediate
constructors
records.Phase 2 - Automatically migrate your code
During Phase 2 you can automatically migrate your code using
dhall lint
, which will automatically replace all occurrences ofconstructors x
withx
now that this is a behavior-preserving transformation.Phase 3 - Your code breaks if you haven’t migrated
During Phase 3 the deprecation cycle is complete and if you haven’t migrated then your code will fail to type-check with an “Unbound variable: constructors” error message.