2015-09-23 29 views
6

Powiedzmy mam klasę:klasa uogólniania Parametry

class C a b t where 
    f :: (a, b) -> (t a, t b) 

Teraz z tej definicji, mogę określić instancje dla słownie:

(a,b) -> (Maybe a, Maybe b) 
(a,b) -> ([a], [b]) 

ale nie dla (o ile rozumiem):

(a,b) -> (a,b) 
(a,b) -> ((a, a), (b, b)) 

mogłem zamiast zmienić definicję klasy tak:

type family T a b x 

class C a b where 
    f :: (a, b) -> (T a b a, T a b b) 

co pozwoliłoby mi zrobić wyżej, ale wtedy byłbym w stanie zadeklarować tylko jeden f dla każdego a i b.

Zasadniczo chcę móc przekazać rodzinę typów jako t w oryginalnej definicji, która jest domyślnie rozstrzygnięta przez moduł sprawdzania typu, o ile jest znany. Nie chcę po prostu zrobić tego jako f :: (a, b) -> (c, d), ponieważ chcę zachować niezmiennik, że zarówno a, jak i b mają to samo zadanie, więc swap . f jest tego samego typu co f . swap. Myślę, że mogę potrzebować injective type families (z GHC 8.0), ale nie jestem pewien. Ale może jest inny sposób?

+1

Brak odpowiedzi: możesz użyć "Tożsamości" w pierwszym przypadku i odpowiedni typ nowego w drugim (teraz nie mogę wymyślić żadnego standardowego). To proste obejście problemu, ale może okazać się mniej bolesne, niż wymaga tego zaawansowana sztuczka. Nawiasem mówiąc, "nie chcę tego po prostu zrobić' f :: (a, b) -> (c, d) 'ponieważ chcę zachować niezmiennik ..." brzmi bardzo podobnie do kwestii Obiektywy * z soczewkami to "Lens Stab" [nawet jeśli te cztery parametry są wzajemnie zależne] (http://comonad.com/reader/2012/mirrored-lenses) (zobacz sekcję "Dlaczego to rodzina soczewek?" Poczta). – duplode

+0

Myślę, że jest to zasadniczo to samo pytanie, co http://stackoverflow.com/questions/4922560/why-doesnt-typesynonyminstancje-wyraz-partially-applied-type-synonyms-to-be-use –

Odpowiedz

5

To może ale nie musi odpowiedzieć na twoje pytanie w zależności od tego, co faktycznie musisz zrobić.

Standardowym rozwiązaniem jest użycie typu new do zdefiniowania nowych instancji. Na przykład.

newtype Pair a = Pair (a, a) 
instance C a b Pair where 
    f = ... 

Jednakże, może to spowodować f mieć Type

f :: (a, b) -> (Pair a, Pair b) 

zamiast poszukiwanego

f :: (a, b) -> ((a, a), (b, b)) 

Te ostatnie mogą być uzyskiwane w wytaczania sposób:

f' :: (a, b) -> ((a, a), (b, b)) 
f' p = case f p of (Pair x, Pair y) -> (x, y) 

Teraz, pisanie funkcji "adaptera", takich jak f', wydaje się zbędne: w końcu po prostu usuwamy opakowanie newtype, które nie zmienia reprezentacji środowiska wykonawczego. Co gorsza, może to być nieefektywne: rozważ przekształcenie [Pair a] w [(a, a)]. Aby to zrobić, musielibyśmy mapować na całej liście, więc płacimy O (n) zrobić zasadniczo nic.

skutecznej alternatywy mogą być konstruowane przy użyciu safe coercions:

import Data.Coerce 

f'' :: forall a b. (a, b) -> ((a, a), (b, b)) 
f'' = coerce (f :: (a, b) -> (Pair a, Pair b)) 

To pozwala na zastosowanie newtype s do kierowania wybór instancji, a jednak je usunąć, gdy w drogę.

Teraz, jeśli twoim celem jest zdefiniowanie nowych instancji bez newtype s, to niewiele pomoże. Jeśli zamiast tego chcesz prosty sposób na usunięcie wrapperów newtype z metod instancji, może to pomóc.

+0

Wygląd 'Data.Coerce' naprawdę interesujące. Dziękuję za tę odpowiedź. –