Disambiguating record updates in the renamer using as-patterns
I think the following could reasonably be expected to work:
{-# LANGUAGE DuplicateRecordFields #-}
module M where
data A a = MkA { fld :: a }
data B b = MkB { fld :: b }
foo x r@(MkA{}) = r { fld = x }
When renaming the record update on the RHS of foo
, the renamer would see r
, and look up in some environment to see it is associated with the pattern MkA{}
which disambiguates which record field fld
refers to.
Currently rnPat
doesn't create any kind of environment to keep track of the connection in an as-pattern:
rnPatAndThen mk (AsPat _ rdr at pat)
= do { new_name <- newPatLName mk rdr
; pat' <- rnLPatAndThen mk pat
; return (AsPat noExtField new_name at pat') }
whereas the typechecker does:
tc_pat pat_ty penv ps_pat thing_inside = case ps_pat of
...
AsPat x (L nm_loc name) at pat -> do
{ ...
; (wrap, bndr_id) <- setSrcSpanA nm_loc (tcPatBndr penv name pat_ty)
; (pat', res) <- tcExtendIdEnv1 name bndr_id $
tc_lpat (pat_ty `scaledSet`(mkCheckExpType $ idType bndr_id))
penv pat thing_inside
; ... }
I think this would be a rather satisfying way to disambiguate record updates, although one shortcoming is that it does not handle sum types very well, as one has to have a separate case for each constructor. We would want something like or-patterns:
data C c = MkC1 { fld1, fld2, fld3 :: c } | MkC2 { fld1, fld2 :: c }
data D d = MkD1 { fld1, fld2 :: d }
-- OK, but requires listing all the constructors
bar1 c1 c2 r@(MkC1{} or MkC2{}) = r { fld1 = c1, fld2 = c2 }
-- Even better, just mention a single constructor!
bar2 c1 c2 r@(MkC1{} or _) = r { fld1 = c1, fld2 = c2 }