Alternatives to convoluted record syntax

Hi Haskellers, This is a style question, as my program works fine but looks really ugly and can be confusing to maintain. I take options from the command line using GetOpt and hand back this structure for later use.
data Opts = Opts { query :: Query , queryLimit :: Maybe Limit , disabledOnly :: Bool } deriving Show
Here's a snippet from the parser for one option (others omitted for clarity):
options :: [OptDescr (Opts -> Opts)] options = [ Option "b" ["bus"] (ReqArg busNum "NUM") "Bus number" , ... ] where busNum n os = let b = (query os) { queryBusNumber = Just n } in if isBusId n then os { query = b } else os
Variations on that ugliness are repeated four times for other fields. Is there an alternative way to change the value of nested fields? Thanks, Dougal. -- Dougal Stanton dougal@dougalstanton.net // http://www.dougalstanton.net

On Thu, 3 Jul 2008, Dougal Stanton wrote:
Here's a snippet from the parser for one option (others omitted for clarity):
options :: [OptDescr (Opts -> Opts)] options = [ Option "b" ["bus"] (ReqArg busNum "NUM") "Bus number" , ... ] where busNum n os = let b = (query os) { queryBusNumber = Just n } in if isBusId n then os { query = b } else os
Variations on that ugliness are repeated four times for other fields. Is there an alternative way to change the value of nested fields?
For access to nested record fields I implemented the record-access package. Unfortunately the field accessors must still be written manually. http://darcs.haskell.org/record-access/src/Data/Accessor/Example.hs E.g.: infix2 :: ((Char, Int), String) infix2 = (('b',7),"hallo")$%first^:second^=10 (second^=10) replaces the second member of pair with a value. (^:) applies that change to the outer record (again a pair). ($%) applies the modifier (first^:second^=10) to a concrete pair.

On Thu, Jul 3, 2008 at 11:13 AM, Henning Thielemann
infix2 :: ((Char, Int), String) infix2 = (('b',7),"hallo")$%first^:second^=10
(second^=10) replaces the second member of pair with a value. (^:) applies that change to the outer record (again a pair). ($%) applies the modifier (first^:second^=10) to a concrete pair.
Hmm! Thanks for the pointer, but I'm not sure that would really clear things up much. It seems this may just be a wart I'll have to put up with. Cheers, D -- Dougal Stanton dougal@dougalstanton.net // http://www.dougalstanton.net

For access to nested record fields I implemented the record-access package. Unfortunately the field accessors must still be written manually. http://darcs.haskell.org/record-access/src/Data/Accessor/Example.hs
I looked for this on hackage, but found instead this: http://hackage.haskell.org/cgi-bin/hackage-scripts/package/data-accessor which, unless I am mistaken, is actually a different package by Luke Palmer, with similar intent. Should we be trying to avoid name overlaps like this? Tim

On Thu, Jul 3, 2008 at 8:00 PM, Dougal Stanton
Here's a snippet from the parser for one option (others omitted for clarity):
options :: [OptDescr (Opts -> Opts)] options = [ Option "b" ["bus"] (ReqArg busNum "NUM") "Bus number" , ... ] where busNum n os = let b = (query os) { queryBusNumber = Just n } in if isBusId n then os { query = b } else os
Variations on that ugliness are repeated four times for other fields. Is there an alternative way to change the value of nested fields?
Here's one suggestion, though whether it's less ugly is questionable. Start with these two handy TH functions: -- \f x -> x { field = f (field x) } alter :: Name -> Q Exp alter field = do f <- newName "f" x <- newName "x" lamE [varP f, varP x] $ recUpdE (varE x) [return (field, AppE (VarE f) (AppE (VarE field) (VarE x)))] -- \a x -> x { field = a } set :: Name -> Q Exp set field = do a <- newName "a" x <- newName "x" lamE [varP a, varP x] $ recUpdE (varE x) [return (field, VarE a)] Now define busNum as follows: busNum n | (isBusId n) = $(modify 'query) ($(set 'queryBusNumber) (Just n)) | otherwise = id The TH brackets and quotes make it look rather cumbersome, but I think the intent at least becomes clearer. Stuart

Hi,
busNum n | (isBusId n) = $(modify 'query) ($(set 'queryBusNumber) (Just n)) | otherwise = id
The solution I am using is creating for each record type @Rec@, and each of its fields @fieldName :: T@ an updater updateFieldName :: (T -> T) -> Rec -> Rec This way you can write busNum n | (isBusId n) = updateQuery $ updateQueryBusNumber $ const (Just n) | otherwise = id The task of creating updaters can be automated using TH, this is what the attached library does: all you need is to say $(genUpdaters ''Opts) $(genUpdaters ''Query) Cheers, Misha
participants (5)
-
Dougal Stanton
-
Henning Thielemann
-
Misha Aizatulin
-
Stuart Cook
-
Tim Docker