I have an extensible Vinyl / Composite record (similar to HList, Frames...), and I would like to generate the tuples of keys/values, such as
tuplify '[String :-> Whatevs, ...] :: [(String, String)]
This is surprisingly hard. original gist.
Solution Gist, thanks to Alec below
type FA = "a" :-> String
type FB = "b" :-> Int
type AB = '[FA, FB]
ab :: Rec Identity AB
ab = "A" :*: 1 :*: RNil
tuplify :: (Show a) => Rec Identity '[a] -> [(String, String)]
tuplify = recordToList . rmap undefined -- ??????
-- tuplify ab = [("a", "A"), ("b", "1")]
If you care to try out what I've done so far, check out that gist, and it has well-thought-out examples and the errors I see:
Here is the hardware for refying in Composite (reifyDicts
):
And the same for Vinyl (reifyConstraints
):
AFAICT, the problem is that in something like rmap
:
rmap :: (forall x. f x -> g x) -> Rec f rs -> Rec g rs
The mapped fn is defined forall x
, but my tuplify
is constrained, and I think the reification should move the constraint into the type (that's what Dict
s are for), but, alas, no luck so far.
I can't get
composite
related stuff to install on my global Stack setup but the following should still work (I just copy-pasted relevant definitions). That said, I think a simple type-class based dispatch based on type is simpler here (since the constraints are non-trivial). With all of the right extensions enabled [1], you just need:Then, in GHCi:
If you really want to try the reifying constraint approach, you'll have to start by declaring a type class and instance for the particular constraint you want:
Then it becomes more straightforward to use
reifyConstraints
andrmap
:I imagine something similar is possible with
reifyDicts
, although I wish there was a variant of it defined usingValuesAllHave
instead of justAllHave
(then we could bypass declaring aShowField
typeclass and do everything in just a function).