EDIT:
It seems that currently the official package for handling record manipulations is purescript-record
- you can find Builder.purs there which provides merge
and build
functions:
> import Data.Record.Builder (build, merge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (build (merge age) name)
{ name :: String
, age :: Int
}
API NOTE:
This API looks overcomplicated at first glance - especially when you compare it to simple unionMerge name age
call (unionMerge
is intoduced at the end of this answer). The reason behind Builder
existence (and so this API) is performance. I can assure you that this:
> build (merge name >>> merge age) {email: "[email protected]"}
creates only one new record. But this:
> unionMerge name (unionMerge age {email: "[email protected]"})
creates two records during execution.
What is even more interesting is how Builder
, build
and merge
are implemented - Builder
is newtype wrapper around a function (and its composition is just a function composition) and build
is just a function application on copied version of the record:
newtype Builder a b = Builder (a -> b)
build (Builder b) r1 = b (copyRecord r1)
In merge
there is unsafeMerge
performed:
merge r2 = Builder \r1 -> unsafeMerge r1 r2
So why are we gaining here anything?? Because we can be sure that intermediate results can't escape function scope and that every value is consumed exactly once in builder chain. Therefore we can perform all transformations "in place" in a mutable manner. In other words this intermediate
value:
> intermediate = unionMerge name {email: "[email protected]"}
> unionMerge age intermediate
can't be "extracted" from here:
> build (merge name >>> merge age) {email: "[email protected]"}
and it is only consumed once by the next builder, namely merge age
.
TYPESYSTEM COMMENT:
It seems that Purescript type system can handle this now thanks to the Union
type class from Prim
:
The Union type class is used to compute the union of two rows
of types (left-biased, including duplicates).
The third type argument represents the union of the first two.
Which has this "magic type" (source: slide 23):
Union r1 r2 r3 | r1 r2 -> r3, r1 r3 -> r2
OLD METHOD (still valid but not preferred):
There is purescript-records package which exposes unionMerge
which does exactly what you want (in new psci we don't have to use let
):
> import Data.Record (unionMerge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (unionMerge age name)
{ name :: String
, age :: Int
}