gleam-lang/gleam

Variant narrowing for constants

Open

#5310 opened on Jan 21, 2026

View on GitHub
 (5 comments) (2 reactions) (0 assignees)Rust (21,417 stars) (960 forks)batch import
help wanted

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.

Contributor guide