What do folks think of a record field projection syntax for PureScript? Something like -
foo = {x:1, y:true, z: Nothing}
bar = foo {.x, .y}
-- bar is now {x:1, y:true}
What do folks think of a record field projection syntax for PureScript? Something like -
foo = {x:1, y:true, z: Nothing}
bar = foo {.x, .y}
-- bar is now {x:1, y:true}
If we’re talking about the feature in general, I think that could be useful in a few contexts. Otherwise, the fastest way to get what you want is
extractXY { x, y } = { x, y }
bar = extractXY foo
If we’re bikeshedding on syntax, that’s another issue altogether. I’d propose:
foo.{x, y}
because I don’t need to deal with the .
character inside the labels, making it easier to copy-paste such records. Moreover, if leading/trailing commas are ever supported, we don’t get IMO some weird code like:
foo {, .x, .y, }
I don’t mind that syntax either, as long as we get the same functionality.
The syntax I proposed follows the existing scheme of eliding the .
operator when updating fields -
Updating a single field - foo.x = 1
(if it were possible to update fields in this way in PureScript)
multiple fields - foo {x = 1, y = 2}
I guess it would be nice to have a consistent syntax for update as well - foo.{x = 1, y = 2}
, but that would break a lot of code out there.
Just as an example of how you might do this in stock PS, because implicit record projections came up in Discord:
https://try.purescript.org/?gist=365396bb83b582cd857e413cc2f963f5
With the optimizer, this can turn into what you would write by hand:
const test = wat => ({baz: wat.baz, foo: wat.foo});
That is amazing, thanks @natefaubion! Perhaps this should go into purescript-record or another well known library.
I’ve been using pick from Records.Extra for this, so you can have:
foo = {x:1, y:true, z: Nothing}
bar :: {x :: Number, y :: Boolean} = pick foo
That implementation of pick
is a little strange to me. It uses the type system to build a List
of string keys, converts it to an Array
, and then passes it off to the FFI to build the record. If an intermediate data structure is built for the keys, you might as well go ahead and build a Record.Builder as your intermediate operation. In my example above, I build a Record immutably, but that’s because it’s a much simpler API and the optimizer handles Record and Record.Builder equally well.
In our codebase we have a function trim
whose type is the same as pick
, but implementation is just trim = unsafeCoerce
Sure it’s not completely honest, because the runtime value retains the extra fields, but works well enough in most cases.