28

After a relatively brief text on Haskell wiki about pattern matching of records, I'm not sure if record's field can be matched with some value and assigned to a parameter at the same time.

-- Sample record type
data Rec = Rec { uid :: Int, name :: String }

-- Function to update Rec's fields
updateRec :: Rec -> Rec

-- basic matching with record data constructor
updateRec (Rec _ []) = Rec 0 "Nobody"

-- dtto with as-pattern at 2nd param
updateRec (Rec idn ns@"Alice") = Rec (idn+1) (ns ++ "+1")

-- basic matching by fields values
updateRec Rec {name = "Bob", uid = 42} = Rec (10^6) "SuperBob"

-- Q: how to match by name field and assign it's value ?
updateRec Rec {name = "Bob", uid = idn} =
    Rec {uid = (idn+1), name = ("Bob" ++ "+1")}

-- Q: as-pattern works only on the whole record, not on a field
updateRec rec@(Rec {name = "Bob", uid = idn}) =
    Rec {uid = (idn+1), name = (name rec ++ "+1")}

-- Q: pattern guards do work, but they are a bit clumsy
updateRec Rec {name = uname, uid = idn}
    | uname == "Bob" = Rec {uid = (idn+1), name = (uname ++ "+1")}
    | otherwise = ...

So is there some concise way to pattern match by record's field and assign its value at the same time?

Note: I would like to avoid specific GHC's extensions.

nbro
  • 15,395
  • 32
  • 113
  • 196
David Unric
  • 7,421
  • 1
  • 37
  • 65

1 Answers1

32

The field names are patterns themselves, so you can use another as-pattern:

updateRec Rec {name = uname@"Bob", uid = idn@42} = ...

There isn't any more concise way to do this, standard or with GHC extensions. Of course, for a case as simple as this, you could simply omit the bindings and use "Bob" and 42 directly in the function body.

nbro
  • 15,395
  • 32
  • 113
  • 196
ehird
  • 40,602
  • 3
  • 180
  • 182