Union refinement permits narrowing branches of tag union pattern matches to the subset of variants not covered by previous branches. For example,

t : [User, Admin, SuperAdmin]
when t is
  User -> ... 
  other -> ... # here, other has type [Admin, SuperAdmin]

This kind of behavior is also known as “type narrowing” and “flow-sensitive typing” in other languages, like TypeScript. The behavior would provide for a subtyping look-and-feel to Roc, where it’s not otherwise. Many Roc users have run into a want for this kind of behavior.

This document describes one approach for type refinement in Roc specific to only refining tag unions in when expressions. Control-flow-sensitive typing in general is not considered. The approach is intended to be “zero-cost” in that it is sugar for what would otherwise be an identical transformation made by a user.

Summary

Inference Scheme

As a quick primer, the inference scheme for pattern matches

when condition is
  pattern_1 -> ...
  pattern_2 -> ...

consists of determining typeof(patterns) and typeof(condition) independently, and then checking that those types are equivalent (making them equal under unification). For example,

t : [User, Admin, SuperAdmin]
when t is
  User -> ... 
  other -> ...

# typeof(condition) = typeof t = [User, Admin, SuperAdmin]
# typeof(patterns)  = typeof User ~ typeof other = [User] ~ * = [User]
# => typeof(t) = [User, Admin, SuperAdmin] ~ [User] = [User, Admin, SuperAdmin]