[GHC] #15855: Warn about incomplete NamedFieldPuns patterns

#15855: Warn about incomplete NamedFieldPuns patterns -------------------------------------+------------------------------------- Reporter: Artyom.Kazak | Owner: (none) Type: feature | Status: new request | Priority: normal | Milestone: Component: Compiler | Version: 8.6.1 Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: None/Unknown Unknown/Multiple | Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- In certain situations it's nice to be able to ensure that record pattern matches are exhaustive: {{{#!haskell data R = R {a, b :: Int} -- should warn f R{a} = a -- should not warn g R{a, b} = a + b -- should not warn h R{a, ..} = a + b -- should not warn i R{a, b = _} = a }}} It's useful when there are several functions that inspect a record (e.g. handlers that update a subset of fields in a database) and we want to make certain that when a new field is added to the record, the programmer won't forget to update functions working with that record (either by handling the field or explicitly discarding it). Currently the only non-hacky way to do it is writing `f (R a _) = ...`, which is obviously not ideal. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15855 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15855: Warn about incomplete NamedFieldPuns patterns -------------------------------------+------------------------------------- Reporter: Artyom.Kazak | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by Artyom.Kazak): A more concrete example: {{{#!haskell -- Handler for POST /update/info updateInfo :: UpdateInfo -> Handler () updateInfo u = do mapM_ setTitle (updateTitle u) mapM_ setDate (updateDate u) -- | Auxiliary data structure used by the handler. -- -- NB: Don't forget to update 'updateInfo' when adding fields here data UpdateInfo = UpdateInfo { updateTitle :: Maybe Text , updateDate :: Maybe UTCTime } instance FromJSON UpdateInfo }}} -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15855#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15855: Warn about incomplete NamedFieldPuns patterns -------------------------------------+------------------------------------- Reporter: Artyom.Kazak | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by simonpj): Can you be more precise about what you are asking for? I think it might be something like: * Warn about any record pattern that does not provide a pattern for every field of the record. What about {{{ g R{a,b} = a g R(a, ..} = a }}} Do those warn? Once we have a precise spec, I don't think this will be hard to implement (in the renamer). I can offer advice. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15855#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#15855: Warn about incomplete NamedFieldPuns patterns -------------------------------------+------------------------------------- Reporter: Artyom.Kazak | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by Artyom.Kazak):
`g R{a,b} = a`
This one is already covered by `-fwarn-unused-matches`.
`g R{a, ..} = a`
This one is trickier, and I'm in two minds about it. Let's see why. ---- Depending on programmer's intent, different kinds of warnings would be useful for the programmer. I will categorize possible intents below, using JSON encoding as a running example. 1. The programmer wants to handle all fields of the record (since the resulting JSON object should contain them all). They write `toJSON R{..} = ...` or `toJSON R{a, b} = ...`. If a new field is added, they would like the compiler to warn them that their `toJSON` implementation needs to be updated. 2. The programmer wants to handle all fields of the record, except for a specific subset – for instance, one of the fields can be derived from the rest of the data and should be omitted from the resulting JSON object to keep the representation compact. They write `toJSON R{a} = ...`, or `toJSON R{a, b = _}`, or perhaps `toJSON R{..} = ...` (relying on the fact that `-fwarn-unused-matches` doesn't apply here). 3. The programmer wants to handle a subset of fields, and they truly don't care about whatever else the record might contain – for instance, when they know that the resulting JSON will be sent to an endpoint that only inspects certain fields. They write `toJSON R{b}` or, again, `toJSON R{..}`. If we want to give helpful warnings in these three cases, we need to somehow distinguish between these different intents. * If we introduce a warning for omitted fields that are not covered by a pattern or `..`, this lets #1 and #2 to be expressed as `R{a, b}` and `R{a, b = _}` respectively. However, this can be cumbersome when there are many fields. * Alternatively, we could introduce a warning for any names brought into scope that are not used in the function body, which lets #1 and #2 to be expressed as `R{..}` and `R{.., b = _}` respectively, but then for #3 the programmer will be forced to list the fields explicitly – e.g. `R{b}`. I like the second option more and it covers my usecases perfectly, but just to make it clear, it's a rather different feature request than the one I originally had in mind. If you agree that the second one has more merit, I will close this ticket and create a new one. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15855#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC