2012-04-17 26 views
26

Używając typów egzystencjalnych, musimy użyć składni dopasowywania do wzorca, aby wyodrębnić wartość ed forall. Nie możemy używać zwykłych selektorów rekordów jako funkcji. GHC zgłasza błąd i sugerują, stosując wzór dopasowywania z tej definicji yALL:Dlaczego nie mogę korzystać z selektorów rekordu z egzystencjalnie kwantyfikowanym typem?

{-# LANGUAGE ExistentialQuantification #-} 

data ALL = forall a. Show a => ALL { theA :: a } 
-- data ok 

xALL :: ALL -> String 
xALL (ALL a) = show a 
-- pattern matching ok 

-- ABOVE: heaven 
-- BELOW: hell 

yALL :: ALL -> String 
yALL all = show $ theA all 
-- record selector failed 

forall.hs:11:19: 
    Cannot use record selector `theA' as a function due to escaped type variables 
    Probable fix: use pattern-matching syntax instead 
    In the second argument of `($)', namely `theA all' 
    In the expression: show $ theA all 
    In an equation for `yALL': yALL all = show $ theA all 

Część moich danych zajmie więcej niż 5 elementów. Trudno utrzymać kod jeśli używam wzorzec-dopasowanie:

func1 (BigData _ _ _ _ elemx _ _) = func2 elemx 

Czy istnieje dobry sposób, aby kod tak utrzymaniu lub owinąć go tak, że mogę użyć jakiegoś selektorów?

+3

Podpowiedź: Jaki byłby typ 'theA'? –

+0

@Louis Wasserman: masz na myśli używanie składni egzystencjalnej w YALL? w jaki sposób? – Nybble

+0

Zasadniczo, odpowiedź jest taka, że ​​nie ma ona typu wyraźnego, więc aby uzyskać działający typ, potrzebne jest dopasowanie wzoru. –

Odpowiedz

15

Można użyć składni rekordu w dopasowywania wzorców,

func1 BigData{ someField = elemx } = func2 elemx 

działa i jest znacznie mniej pisania dla wielkich typów.

+0

Świetna odpowiedź! Właśnie znalazłem to później: dane B {x :: Int, y :: Int}; zabawa B {..} = x + y; to może być alternatywa. – Nybble

16

Typy egzystencjalne działają w bardziej złożony sposób niż zwykłe typy. GHC (słusznie) zabrania używania funkcji theA. Ale wyobraź sobie, że nie było takiego zakazu. Jaki rodzaj miałaby ta funkcja? To musiałoby być coś takiego:

-- Not a real type signature! 
theA :: ALL -> t -- for a fresh type t on each use of theA; t is an instance of Show 

Mówiąc bardzo prymitywnie, forall sprawia GHC „zapomnieć” typ argumentów konstruktora; wszystko, co system typów wie, jest to, że ten typ jest instancją Show. Tak więc, gdy próbujesz wyodrębnić wartość argumentu konstruktora, nie ma możliwości odzyskania oryginalnego typu.

Co robi GHC za kulisami, jest to, co mówi komentarz do powyższego podpisu fałszywego typu - za każdym razem, gdy dopasowujesz wzór do konstruktora ALL, zmiennej przypisanej do wartości konstruktora przypisywany jest unikalny typ, który gwarantuje, że jest różni się od każdego innego typu. Weźmy na przykład ten kod:

case ALL "foo" of 
    ALL x -> show x 

Zmienna x otrzymuje unikalny typ, który jest różny od każdego innego typu w programie i nie może być dopasowana do każdej zmiennej typu. Te unikalne typy nie mogą uciec na najwyższy poziom, co powoduje, że theA nie może być używany jako funkcja.

+5

Ogólnie można myśleć o typie egzystencjalnym jako zależnej krotce, gdzie pierwszym elementem jest typ ('*'), a drugim jest wartość tego typu - 'Σ [a: *] a'. Problem polega na tym, że przy próbie napisania podpisu typu dla projekcji drugiego elementu krotki, musisz znać wartość pierwszego elementu. Nie można tego wyrazić w Haskell. Jeśli masz zależnie napisany język, możesz zapisać go jako (używając notacji Agdy): '(x: Σ [a: *] a) → fst x'. – Vitus

+4

+1 Wyjaśnia to komunikat o błędzie "Nie można użyć selektora rekordu" theA "jako funkcji ** ze względu na zmienne typu Escaped **" –

Powiązane problemy