gren-lang / gren-lang/compiler
exhaustive record pattern
Nobody has claimed this yet.
- Dominant language
- Haskell
- Stars
- 503
- Forks
- 29
- PR merge metrics
- No merged PRs in 30d
Description
Proposing syntax to make sure you've looked at all the fields of a record value. Adding or removing a field should in that case warn you that you need to explicitly ignore/use it.
Some common use cases
- matching on info (from type aliases) in view/render functions, deciding what should be shown and what not (e.g. viewing a list of tasks which might get creation date, tags, completion etc in the future)
- a declaration taking a record of arguments which should all be acknowledged (e.g.
Parser.numberchecking all arguments) - encoding where not encoding a field should be an explicit choice (e.g. encoding an audio graph for a js library)
- a let destructuring all of the values returned from a case-of or if
pattern syntax
{ _ | command = commandValue }for inexhaustive match with explicit variable name (equivalent to current{ command = commandValue }){ _ | command }for inexhaustive match with field value variable name inherited from field name (equivalent to current{ command }){ model = modelValue, command = commandValue }for exhaustive match using all field values{ model = modelValue, command = _ }for exhaustive match ignoring a field value{ model = modelValue, command }for exhaustive match using all field values, with a field value variable name inherited from field name
This is consistent with {} being a match on a record with exactly 0 fields, instead of a record with whatever fields.
example code
update msg model =
case msg of
UserSelectedTrackColor { trackIndex = trackIndexToRecolor, hue = selectedNewTrackHue } ->
{ model
| tracks =
model.tracks
|> Array.update trackIndexToRecolor
(\track -> { track | hue = selectedNewTrackHue })
}
type Msg =
| UserSelectedTrackColor { trackIndex : Int, hue : Float }
if we then e.g. add
type Msg =
| UserSelectedTrackColor { trackIndex : Int, hue : Float, lightness : Float }
the compiler will give an error about lightness not being handled in the update case branch.
Had this been an inexhaustive record match as in current gren, the user choice of lightness would simply be ignored, which is a pretty subtle bug!
similar mindset in existing features
- using
\{} ->instead of\_ ->for laziness to avoid accidentally ignoring future values. E.g. changing fromTest.testtoTest.fuzzshould trigger a compiler error that you didn't use the fuzzed value - using the value
{ contextA = old.contextA, contextB = [] }instead of{ old | contextB = [] }when you want to assure yourself you've considered for each field whether it needs changing or not - not using a catch-all case when adding variants could affect future logic
conflicts
The proposed exhaustive record pattern does not allow matching on a value of an extensible record type.
This prevents a coding style where functions like view take info as an extensible record (also slightly related: https://github.com/gren-lang/compiler/issues/240).
alternatives considered
- (only partial solution) catch unused extensible record argument fields in the compiler (or a widely used static analysis tool). This might prevent type magicians from using some extra fields for type-level permissions, context etc
current workaround
Explicitly add a let declaration for the record with an explicit record fields type annotation which only has the fields you've considered.
discussion
This issue is mostly a summary of my points brought up in this discord thread
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing the proposed exhaustive record-pattern syntax, examples, conflicts, and current workaround in this issue. No implementation files, tests, or entry points are named; done means implementing the syntax and compiler diagnostics so added record fields must be explicitly used or ignored.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100