EDIT:
It seems that at present, the official package for processing records is purescript-record
- you can find Builder.purs there , which provides the functions merge
and build
:
> import Data.Record.Builder (build, merge) > name = {name: "Jim"} > age = {age: 37} > :t (build (merge age) name) { name :: String , age :: Int }
API NOTE:
At first glance, this API looks too unionMerge name age
especially if you compare it with a simple unionMerge name age
unionMerge
( unionMerge
is entered at the end of this answer). The reason for the existence of Builder
(and therefore this API) is performance. I can assure you that it is:
> build (merge name >>> merge age) {email: " someone@example.co m"}
creates only one new record. But this:
> unionMerge name (unionMerge age {email: " someone@example.com "})
creates two records at runtime.
What is even more interesting is how Builder
, build
and merge
are implemented - Builder
is a new type of shell around a function (and its layout is just a function layout), and build
is just a function application to the copied version of the record:
newtype Builder ab = Builder (a -> b) build (Builder b) r1 = b (copyRecord r1)
When merge
is executed, unsafeMerge
is unsafeMerge
:
merge r2 = Builder \r1 -> unsafeMerge r1 r2
So why are we getting something ?? Because we can be sure that intermediate results cannot leave the scope of the function and that ever the value is used exactly once. To do this, we can perform all the transformations in a mutable manner "in place". In other words, this is an intermediate
value:
> intermediate = unionMerge name {email: " someone@example.com "} > unionMerge age intermediate
cannot be "extracted" from here:
> build (merge name >>> merge age) {email: " someone@example.com "}
and is used only by the next builder, namely merge age
.
TYPESYSTEM COMMENT:
It looks like the Purescript type system can handle this now thanks to the Prim
type class of Union
:
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 a purescript-records package that provides unionMerge
which does exactly what you want (in the new psci we don’t need to use let
):
> import Data.Record (unionMerge) > name = {name: "Jim"} > age = {age: 37} > :t (unionMerge age name) { name :: String , age :: Int }