Alternatives to convoluted record syntax

View: New views
6 Messages — Rating Filter:   Alert me  

Alternatives to convoluted record syntax

by Dougal Stanton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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@... // http://www.dougalstanton.net
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: Alternatives to convoluted record syntax

by Henning Thielemann :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


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.
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: Alternatives to convoluted record syntax

by Dougal Stanton-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, Jul 3, 2008 at 11:13 AM, Henning Thielemann
<lemming@...> wrote:

>
> 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@... // http://www.dougalstanton.net
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: Alternatives to convoluted record syntax

by Stuart Cook :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, Jul 3, 2008 at 8:00 PM, Dougal Stanton <dougal@...> 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?

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
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: Alternatives to convoluted record syntax

by Tim Docker-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


> 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

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: Alternatives to convoluted record syntax

by Misha Aizatulin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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



_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

RecordUpdate.tar.gz (5K) Download Attachment
LightInTheBox - Buy quality products at wholesale price