3

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.

Community
  • 1
  • 1
jamshidh
  • 12,002
  • 17
  • 31
  • 1
    You could look in to how the Aeson library solves this with automatic conversions to and from JSON for types implementing `Generic`. – bheklilr Nov 06 '13 at 19:04
  • 3
    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 Nov 06 '13 at 19:46
  • 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 Nov 06 '13 at 20:51
  • First, look here: http://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/Plan . May be it would be useful – wit Nov 06 '13 at 21:52
  • Related: http://stackoverflow.com/questions/8659939/haskell-setting-record-field-based-on-field-name-string – Rehno Lindeque Mar 25 '14 at 09:11

1 Answers1

3

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.

J. Abrahamson
  • 72,246
  • 9
  • 135
  • 180
  • I did a bit of research on Vinyl, and it doesn't seem to do what I was asking about. The strings that appear in the type are actually dropped at compile time, so this means that they can not be used to set a record using a runtime string (am I missing something?). The library looks cool, but isn't pertinent. Also, I disagree that a statically typed record confers no advantage beyond a uniformly typed Map. The idea is to set the types of the fields immedietely, thus reaping the benefits of static typing for most of the program. – jamshidh Nov 13 '13 at 19:19
  • You want phase mixing, I think. If you want runtime strings to be valid map keys then you won't be able to typecheck to ensure that those are proper at compile time. Vinyl's records do just the opposite—they're checkable at compile time and thus are erasable. Perhaps you could convert a homogenous Vinyl `Record` to a `Map` during runtime to mix those modes, but you won't have type-safety for the runtime components. – J. Abrahamson Nov 13 '13 at 19:41
  • If you want to reflect runtime strings up into the type system and ensure that they're used uniquely, you can *perhaps* get away with this by using a phantom type parameter trick similar to `ST`'s `s` parameter. I don't think you'll be able to create type-level evidence that two runtime strings are identical, though. – J. Abrahamson Nov 13 '13 at 19:45