2011-07-19 14 views
9

Rozważmy następujący typu Klasa:instancje typu klasy dla typów z 2 parametrami, gdy typ-klasa ma tylko jedną

class Listable a where 
    asList :: a t -> [t] 

Jest to dość łatwe do utworzenia instancji dla typów z jednego parametru:

instance Listable [] where 
    asList = id 

instance Listable Maybe where 
    asList (Just x) = [x] 
    asList Nothing = [] 

Jak mam utworzyć instancję dla pary z dwoma identycznymi parametrami? Oczywiście mógłbym zrobić Opakowanie:

data V2 a = V2 a a 

v2 (p,q) = V2 p q 

instance Listable V2 where 
    asList (V2 p q) = [p,q] 

Teraz mógłbym napisać rzeczy jak asList $ v2 (47, 11), ale tego rodzaju celowość.

Czy istnieje sposób na ograniczenie typu pary do przypadków, w których oba parametry są takie same, i na to, aby napisać instancję Listable? Jeśli nie, jakie jest zwykle obejście tego problemu?

Odpowiedz

13

Istnieje wiele sposobów na zrobienie tego, koncepcyjnie. Niestety, większość z nich nie działa. Niestety!

Po pierwsze, jako programista funkcjonalnej, Założę się, że to jest to, czego naprawdęchcesz napisać:

instance Listable (\a -> (a, a)) where 
    asList (p, q) = [p,q] 

Niestety wpisać szczebla lambdas nie istnieją. Moglibyśmy napisać o nazwie wersję powyższej lambda przy użyciu typu synonim:

type Same2 f a = f a a 

instance Listable (Same2 (,)) where { ... } 

Że nie wolno albo, ponieważ typ synonimem nie jest w pełni stosowane. Mogliśmy zamiast wyobrazić klasę typu biorąc dodatkowy argument, który opisuje, jak zastosować zmienne typ:

class Listable app f where 
    asList :: app f a -> [a] 

instance Listable __ Maybe where { ... } 

instance Listable __ (,) where { ... } 

nawet bez myślenia o tym, co app może być, to również nie dlatego, że nie mają spójnego rzeczowe dla parametr f.

Przechodząc do rzeczy, które rzeczywiście działają, myślę, że najbardziej popularnym sposobem jest zawarcie podejścia synonimów typu wewnątrz newtype, a następnie poradzenie sobie z zawijaniem i rozpakowywaniem, które obejmuje.

newtype Same2 f a = Same2 (f a a) 

instance Listable (Same2 (,)) where { ... } 

Jest funkcjonalny, jeśli nieco brzydki. Możesz również w ten sposób zdefiniować kompozycję konstruktora typu i inne zabawki, a następnie odejść z pozbawionymi punktów tekstem typami ukrytymi pod stosem skoczni.

W końcowym podejściu, można również zakodować podejście powyżej lambda w stylu „w odwrotnym kierunku”, przechodząc z wersja w pełni stosowane, do parametr pojedynczy typ:

class Listable t where 
    type ListableElem t :: * 
    asList :: t -> [ListableElem t] 

Being umiejętność robienia tego typu rzeczy jest jedną z głównych motywacji rodzin typów. To samo można wyrazić za pomocą MPTC i fundeps, ale jest to 1) równoważne i 2) o wiele brzydsze, więc nie będę się tym przejmował.

+0

Przepraszamy za bycie leniwym, aby go wypróbować, ale ostatnie podejście pozwoliłoby na "Listable (a, a)" - czyż nie? – yatima2975

+0

@ yatima2975: Kosztem posiadania różnych instancji z '(,)' jako najbardziej zewnętrznym konstruktorem typów, tak. Prawdopodobnie powinienem podać to jako przykład w mojej odpowiedzi, teraz, gdy o tym wspomniałeś ... –

2

Musisz zdefiniować typ opakowania, aby to zrobić. (Powinieneś jednak użyć newtype.) Możesz nawet zdefiniować:

newtype Foo t a = Foo(t a a) 

instance Listable (Foo (,)) where 
    asList (Foo (a,b)) = [a,b] 
Powiązane problemy