2016-03-14 12 views
5

Dato che ho i record folowing in purescript:Combina Records nel Purescript

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

E 'possibile combinare questi due dischi in qualche modo in modo generico? Qualcosa di simile:

name 'comb' age 

tale che ricevo il seguente record:

{name: "Jim", age: 37} 

In qualche modo sembra essere possibile con l'rowtype Eff, ma io sono curioso di sapere se sarebbe possibile con 'normale 'record. Sono nuovo di purescript ed è la sintassi dei record.

Grazie mille.

risposta

5

Al momento non è possibile farlo, poiché non abbiamo un modo di dire che una riga manchi di qualche etichetta. E 'possibile avere un tipo di record aperto:

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

Ma questo ci consente solo di accettare un record con name ed eventuali altre etichette, che non ci aiuta se vogliamo unire, estendere, o sottrarre dai record così com'è.

Il problema con la combinazione record arbitrari è che avremmo un firma di tipo come questo:

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

Abbiamo bisogno di un modo per dire che il risultato (???) è l'unione di r1 e r2, ma anche noi Forse vorremmo dire che le etichettenon si sovrappongono a quelle di r2.

In futuro questo potrebbe essere possibile tramite row constraints.

+0

Cool, grazie per la spiegazione. Ma solo per chiarire un po 'le cose: come funziona con il rowtype Eff. Per quanto ho capito, il tipo di riga Eff è un po 'come "composto" da diversi tipi di effetti. Come funziona lì? –

+0

Si basa sui tipi che sono unificati, quindi nell'esempio sopra, se chiamiamo 'something' con' {name :: String, age :: Int, address :: String} 'finiamo con' r ~ (age: : Int, address :: String) 'in' {name :: String | r} '. La roba 'Eff' funziona in modo simile, non abbiamo mai realmente qualcosa in cui due diversi valori di' eff' vengono combinati da argomenti diversi per produrne uno nuovo. –

+0

ok, penso di aver capito. Molte grazie. –

7

EDIT:

E seems che attualmente il pacchetto ufficiale per la gestione di manipolazioni record è purescript-record - si possono trovare Builder.purs lì che fornisce merge e build funzioni:

> import Data.Record.Builder (build, merge) 
> name = {name: "Jim"} 
> age = {age: 37} 
> :t (build (merge age) name) 
{ name :: String 
, age :: Int 
} 

API NOTA:

Questa API sembra complicata a prima vista, specialmente quando la si confronta con la semplice chiamata unionMerge name age (unionMerge è intodotto alla fine di questa risposta). Il motivo alla base dell'esistenza di Builder (e quindi di questa API) è la prestazione. Posso assicurarti che questo:

> build (merge name >>> merge age) {email: "[email protected]"} 

crea solo un nuovo record. Ma questo:

> unionMerge name (unionMerge age {email: "[email protected]"}) 

crea due record durante l'esecuzione.

Ciò che è ancora più interessante è come sono implementati Builder, build e merge - Builder è Newtype wrapper per la funzione (e la sua composizione è solo la composizione di funzione) e build è solo funzione di applicazione sulla versione copiata di un record:

newtype Builder a b = Builder (a -> b) 

build (Builder b) r1 = b (copyRecord r1) 

In merge c'è unsafeMerge eseguita:

merge r2 = Builder \r1 -> unsafeMerge r1 r2 

Quindi w sto guadagnando qualcosa ?? Perché possiamo essere sicuri che i risultati intermedi non possono sfuggire dall'ambito della funzione, quindi tutte le trasformazioni possono essere eseguite sul posto. In altre parole questo valore intermediate:

> intermediate = unionMerge name {email: "[email protected]"} 
> unionMerge age intermediate 

non può essere "estratta" da qui:

> build (merge name >>> merge age) {email: "[email protected]"} 

sistema dei tipi COMMENTO:

Sembra che tipo di sistema Purescript può gestire questa ora grazie la classe Union tipo da 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. 

Che ha questo "tipo di magia" (fonte: slide 23):

Union r1 r2 r3 | r1 r2 -> r3, r1 r3 -> r2 

vecchio metodo (ancora valido ma non preferita):

C'è purescript-records pacchetto che espone unionMerge che fa esattamente quello che voglio (nel nuovo psci non dobbiamo usare let):

> import Data.Record (unionMerge) 
> name = {name: "Jim"} 
> age = {age: 37} 
> :t (unionMerge age name) 
{ name :: String 
, age :: Int 
}