I’ve just released an initial version of https://github.com/natefaubion/purescript-heterogeneous which aims to make writing maps and folds over stuff like Record a lot easier and more approachable. I’ve written up a short README which covers the basics of using it (with Records at least), and I’ve included some examples in the tests. One is an implementation of HList which supports hfoldlWithIndex with Peano numerals for indexing.
Some interesting points/issues:
Never write boilerplate Row.Cons instances again!
The fundeps are very flexible (and thus inference may not be as good). One example is that you can write type-aligned folds where the accumulator type changes with every step.
Inference is weird for the bare function case, so you have to make sure its monomorphic. Maybe related to fundeps?
Looks like I should bring some of this into Formless – so many boilerplate classes for various transformations. I’ll give it a try when I have some time – probably this weekend – and give you some feedback as I work!
I tried using this but I failed, probably because of my lack of knowledge with the concepts used.
What I wanted to do was (I was actually using it with Formless!) this kind of transformation:
{foo :: { input :: a }, bar :: { input :: b }, baz :: { input :: c } } -> {foo :: a, bar :: b, baz :: c}
I tried using FoldingWithIndex and Record.build. I had to modify FoldingWithIndex to add a functional dependecy f -> y, but after a while I got stuck on other errors and since I was mostly trying stuff at random I gave up
I’m pasting here the messy code:
data GetInput = GetInput
instance getInput
:: ( IsSymbol sym
, Row.Cons sym b a c
, Row.Lacks sym a
)
=> FoldingWithIndex GetInput (SProxy sym) (Record a) (Record (input :: b)) (Record c) where
foldingWithIndex GetInput prop record a =
Builder.build (Builder.insert prop a.input) record
getInput' :: forall a r t. HFoldlWithIndex GetInput (Record a) { | r } (Record a)
=> RowToList a t => { | r} -> Record a
getInput' r = hfoldlWithIndex GetInput ??? r -- <- no idea how to make this work
testGetInput :: { a :: Int, b :: String }
testGetInput =
getInput' { a: { input: 3 }, b: { input: "foo" } }