3
votes

In many cases it would be useful to be able to set/get a record field via its name as a string (for instance, converting records to/from command line arguments, HTTP headers, SQL query results, or store widgets in a GUI tree in an easy to use record, etc). The functions could have the types

setField::String->Val->Record->Record
getField::String->Record->Val
getFieldNames::Record->[String]

where Val could be something that could convert to other types (string, glib GValue, or even the Convertables I described here)

I've started writing a library that does this, with plans contribute it to the community (it will need some GHC extensions, probably including TemplateHaskell)....

My question- Does something like this already exist? Is there a better way to populate Records from external name/value pairs? I've looked all over and can't find it. (lenses are related, but don't use the string name).

I think this would be useful, but don't want to reinvent the wheel.

1
You could look in to how the Aeson library solves this with automatic conversions to and from JSON for types implementing Generic.bheklilr
This seems like a bad idea.. what happens when the key doesn't exist? No-op? Maybe type? What benefit does this provide over a wrapper around Map? It's reasonable to reify to and from Record and some record type (see aeson for example) but the record type itself can be dumb.Daniel Gratzer
Jozefg- These are problems that any command line argument/SQL query result/widget collection library have to deal with, but right now all this has to be redone by hand every time it is used (ie- code has to be written just to get System.Console.GetOpt working, wouldn't you rather just use a prewritten function wrapped around a single record). Of course you could provide a Maybe version of the same function (like head vs. listToMaybe). The point is, I would rather have a library deal with these error cases than have to remember to deal with it myself every time.jamshidh
First, look here: ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/… . May be it would be usefulwit

1 Answers

3
votes

You might do something like this using Vinyl, though you'll still need to create strongly type accessors. The advantage is that the type system contains enough information to ensure that you never need to runtime handle Maybe-wrapped lookup failures.

Copying some relevant examples from that link

name     = Field :: "name"     ::: String
age      = Field :: "age"      ::: Int
sleeping = Field :: "sleeping" ::: Bool


jon = name =: "jon"
  <+> age =: 20
  <+> sleeping =: True

type LifeForm = ["name" ::: String, "age" ::: Int, "sleeping" ::: Bool]

jon :: PlainRec LifeForm

wakeUp :: (("sleeping" ::: Bool) ∈ fields) => PlainRec fields -> PlainRec fields
wakeUp = sleeping `rPut` False

jon' = wakeUp jon

> rGet name jon'
"jon"
> rGet sleeping jon
True
> rGet sleeping jon'
False

If you're not willing to do something akin to this, then you're probably going to end up with some kind of runtime failure which means you might as well have type Record = Map String Val.