Purescript combo entries

Given that I have the following entries in purescript:

let name = {name: "Jim"} let age = {age: 37} 

Is it possible to combine these two records in some general way? Sort of:

 name 'comb' age 

to get the following entry:

 {name: "Jim", age: 37} 

Somehow this seems possible with the Eff strings string, but I'm curious if this is possible with "normal" entries. I am new to purescript and write syntax.

Thank you very much.

+6
source share
2 answers

This cannot be done at the moment, since we have no way of saying that there is no label or other in the line. It is possible to have an open record type:

 something :: forall r. { name :: String | r } -> ... 

But this only allows us to accept the record with name and any other labels, it will not help us if we want to combine, extend or subtract from the records in the form in which they are.

The problem with combining arbitrary records is that we have a signature like:

 comb :: forall r1 r2. { | r1 } -> { | r2 } -> ??? 

We need to somehow say that the result ( ??? ) is the union of r1 and r2 , but we also want to say that the labels r1 do not intersect with r2 's.

This is possible in the future with string restrictions .

+5
source

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 } 
+9
source

Source: https://habr.com/ru/post/1245003/


All Articles