2016-03-14 11 views
5

Biorąc mam folowing rekordy w purescript:Kombajny Records w Purescript

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

to możliwe, aby połączyć te dwa rekordy jak niektórzy w sposób ogólny? Coś jak:

name 'comb' age 

tak, że pojawia się następujący zapis:

{name: "Jim", age: 37} 

Jakoś wydaje się być możliwe z EFR rowtype, ale jestem ciekaw, czy byłoby możliwe „normalne "zapisy. Jestem nowy w purescript i jego rekordowej składni.

Wielkie dzięki.

Odpowiedz

5

Nie można tego zrobić w tej chwili, ponieważ nie mamy sposobu, aby powiedzieć, że w wierszu brakuje jakiejś etykiety lub innej. Jest możliwe, aby mieć otwarty typ rekordu:

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

Ale to tylko pozwala nam zaakceptować rekord name i wszelkie inne etykiety, to nie pomoże nam, jeśli chcemy połączyć, rozszerzyć lub odejmować z zapisów w obecnym stanie.

Problem z łączenia dowolnych rejestrów jest musielibyśmy podpis typu takiego:

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

potrzebujemy sposób powiedzieć wynik (???) jest sumą r1 i r2, ale również my Być może chcesz powiedzieć, że etykiety r1 nie pokrywają się z etykietami.

W przyszłości może to być możliwe za pośrednictwem row constraints.

+0

Fajnie, dziękuję za wyjaśnienie. Ale żeby trochę wyjaśnić sprawy: Jak to działa z wierszem Eff. O ile rozumiem, wiersz typu Eff jest jak "skomponowany" z różnych typów efektów. Jak tam działa? –

+0

Opiera się na zunifikowanych typach, więc w powyższym przykładzie, jeśli nazwiemy 'coś' z' {name :: String, age :: Int, address :: String} ', otrzymamy' r ~ (age: : Int, adres :: String) 'in' {nazwa :: String | r} '. Komponenty 'Eff' działają w podobny sposób, nigdy nie mamy w rzeczywistości czegoś, w czym dwie różne wartości' eff' są łączone z różnych argumentów w celu utworzenia nowego. –

+0

OK, myślę, że nie. Wielkie dzięki. –

7

EDIT:

To seems że obecnie oficjalny pakiet do obsługi rekordowe manipulacje jest purescript-record - można znaleźć Builder.purs tam, co zapewnia merge i build funkcje:

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

API UWAGA:

Ten interfejs API wygląda na pierwszy rzut oka zbyt skomplikowany - zwłaszcza gdy porównasz go do prostego połączenia unionMerge name age (unionMerge zostaje wprowadzony na końcu tej odpowiedzi). Powodem istnienia Builder istnienia (a więc tego API) jest wydajność. Zapewniam Cię, że:

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

tworzy tylko jeden nowy rekord. Ale to:

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

tworzy dwie zapisy podczas wykonywania.

Co jest jeszcze bardziej interesujące jest to, jak Builder, build i merge są realizowane - Builder jest newtype otoki wokół funkcji (i jego skład jest tylko złożenie funkcji) i build jest tylko funkcja aplikacji na skopiowanej wersji rekordu:

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

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

W merge jest unsafeMerge wykonywane:

merge r2 = Builder \r1 -> unsafeMerge r1 r2 

Więc w hy zyskujemy cokolwiek ?? Ponieważ możemy być pewni, że wyniki pośrednie nie mogą wymknąć się zakresowi funkcji, więc cała transformacja może zostać przeprowadzona na miejscu. Innymi słowy intermediate wartość:

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

nie może być "ekstrakcji" stąd:

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

system typów Komentarz:

Wydaje się, że układ typu Purescript może obsłużyć tego teraz dzięki klasa typu Union z 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. 

który ma tę "magiczną type" (źródło: slide 23):

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

stara metoda (nadal ważna, ale nie zalecane):

Jest purescript-records pakiet, który naraża unionMerge który robi dokładnie to, co chce (w nowych psci nie musimy używać let):

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