Skip to content

Narrow record-type unions on their literal discriminant key - #2313

Open
dak2 wants to merge 4 commits into
soutaro:masterfrom
dak2:record-union-narrowing
Open

dak2 wants to merge 4 commits into
soutaro:masterfrom
dak2:record-union-narrowing

Conversation

@dak2

@dak2 dak2 commented Sep 22, 2026

Copy link
Copy Markdown

Summary

Narrowing hash[:type] via if/unless/case-when now also narrows hash itself, when hash is a union of record types.

Details

Given hash: a | b, where a/b are record types with a shared literal discriminant key, checking that key's value narrows hash to the member whose key type matches:

  • if hash[:type] == "A" — narrows hash to a in the truthy branch, b in the falsy branch
  • case hash[:type] when "A" — narrows hash to a in that when clause, and to the bottom type in an exhaustive else
  • optional discriminant keys (?type: "A") are treated as "A" | nil

This lets you access member-specific fields (e.g. hash[:position]) right after the branch, without an extra cast.

Not supported

Narrowing is skipped entirely, not partially applied, when the discriminant key's value type is:

  • a Union of literals
  • a nested record
  • reached through an alias

For this PR scope, I want to keep the specifications as simple as possible.

dak2 and others added 2 commits September 22, 2026 18:56
A local variable holding a union of record types is now narrowed
when its literal discriminant key is refined, e.g. by
`if event[:type] == "CLICK"`. Previously only the pure call
`event[:type]` was narrowed; `event` itself stayed the full union.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`case event[:type] when "CLICK"` now narrows `event` the same way
`if`/`unless` already did.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@soutaro soutaro self-assigned this Sep 24, 2026
dak2 and others added 2 commits September 24, 2026 17:30
Narrowing went through `refine_types`, which drops every pure call on
the variable as if it were reassigned, so `x[:name]` narrowed by an
earlier guard reverted to its declared type.

Add `TypeEnv#narrow_types`, which keeps them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@dak2
dak2 force-pushed the record-union-narrowing branch from 0d7de0f to b05b092 Compare October 8, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants