Description
Type refinement currently works for constants inside case expressions, but only when the pattern explicitly matches the record constructor (not in all patterns). Matching on a constant and explicitly using the record constructor in the pattern allows accessing fields specific to that variant within that specific branch.
Since the value of a module constant is known at compile time, the compiler can guarantee which variant it holds. Therefore, accessing fields that exist on that specific variant should be type-safe globally, without requiring a case unwrapping or a specific pattern match.
Code examples
Current working behavior (Refinement in specific case):
Gleam playground
pub type Wibble {
Wibble
Wobble(int: Int)
}
const wobble = Wobble(42)
pub fn main() {
// This works today
case wobble {
Wibble -> 24
Wobble(_) -> wobble.int // Accessing field on the constant after matching
}
}
(Note: this generates invalid Erlang as reported in #5261, but works for the JavaScript target.)
Proposed behavior (Global refinement): Gleam playground
pub type Wibble {
Wibble
Wobble(int: Int)
}
const wobble = Wobble(42)
pub fn main() {
// Proposed: This should compile because 'wobble' is known to be 'Wobble' at compile time.
wobble.int
}
Currently, this produces the following error:
error: Unknown record field
┌─ /src/main.gleam:10:10
│
10 │ wobble.int
│ ^^^ This field does not exist
The value being accessed has this type:
Wibble
It does not have fields that are common across all variants.
Note: The field you are trying to access is not defined consistently across
all variants of this custom type. To fix this, ensure that all variants
include the field with the same name, position, and type.