2012-09-09 11 views
6

Rozpocząłem to nowe pytanie, ponieważ stało się kontynuacją poprzedniego pytania.Używanie typów danych w Haskell

Jeśli mam dwa typy danych, które składają się z podobnych wykonawców:

data A = X | Y | Z 
data B = X | Y 

istnieje żaden sposób nie mogę jakoś reprezentować to jako:

data A = C | Z 
data B = C 

data C = X | Y 

jeśli można zobaczyć, co robię - Próbuję zgrupować X | Y w jeden typ danych, który może następnie zostać użyty przez wiele innych typów danych. Nie mogę sprawić, żeby kompilator zezwolił na to, lub jeśli tak, nie mogę dopasować wzorca do X lub Y, tylko do C ??

Otrzymuję komunikat o błędzie, że C zostało zadeklarowane wiele razy.

Pomyślałem, że mógłbym użyć typów, ale nie pozwalają na wiele typowań.

EDIT

Nawet jeśli Oświadczam długą drogę (jak poniżej), to nadal nie będzie kompilować i mówi, X i Y mają wielokrotne deklaracje:

data A = X | Y | Z 
data B = X | Y 
+0

Co pytasz o byłby podtyp 'A '. Nie zostałby zadeklarowany ze słowem kluczowym "dane", które tworzy nowy typ, rozłączny z wcześniejszymi typami. Nie sądzę, że Haskell ma jakąś taką funkcję, ale nie jestem zwolennikiem wszystkich rozszerzeń Haskella. – Gilles

+0

@Gilles: Nie, Haskell nie ma żadnego polimorfizmu podtypu. Ma tylko parametryczny polimorfizm i polimorfizm ad-hoc poprzez klasy typów. Najbliższą rzeczą, jaką można uzyskać, jest typ egzystencjalny, ale to prawie, ale nie całkiem, zupełnie inna sprawa. –

+0

Powiedziałabym to jako odpowiedź, ale ponieważ nie jest całkiem .. Możesz być w stanie zbliżyć się do tego, co chcesz, deklarując typografię, a następnie operacje, które są potrzebne do tych "zwykłych rzeczy". Jest to dość popularny sposób odwracania rzeczy do rozwiązania (wersja) tego problemu .. –

Odpowiedz

13

Nie tylko nie można zrobić to również nie możesz zrobić pierwszej opcji - tzn. nie możesz mieć dwóch typów w tym samym module, który ma konstruktorów o nazwach X i Y.

Jeśli mógłbyś to zrobić, jaki powinien być typ X być - C, lub B? Najbardziej oczywistą odpowiedzią byłby C, ale wtedy nie byłbyś w stanie użyć go w kontekście, w którym wymagane są A lub B (zauważ, że Haskell nie ma podtytułów), tak by pokonać cel całego konstruktu.

Najlepsze co możesz zrobić, to owinąć C w konstruktorze A i B, czyli:

data A = AC C | Z 
data B = BC C 
data C = X | Y 

Następnie można owinąć C z albo AC lub konstruktor BC stworzyć wartość typu A lub odpowiednio B.

+0

Po prostu edytowane, aby powiedzieć, że moja pierwsza opcja nie działa. Kiedy mówisz "wrap", masz na myśli "dołączenie innej części składni przed C, aby odróżnić od oryginalnego C"? – Lethi

+2

@Dan Tak. W moim przykładzie wpisz "AC X", aby utworzyć wartość typu "A" i "BC X", aby utworzyć wartość typu "B". 'X' samo nie może mieć dwóch różnych typów. – sepp2k

4

Powodem nie można zrobić tego

data A = X | Y | Z 
data B = X | Y 

jest następujący. Powiedzieć, napisać trochę kodu później:

foo n = (n,X) 

która buduje parę składającą się z n w pierwszym gnieździe i X w drugim gnieździe. Jakiego rodzaju powinien wywnioskować kompilator? Prawidłowy typ byłoby

foo :: a -> A -> (a,A) 

od X jest konstruktorem typu A, ale równie ważna jest

foo :: a -> B -> (a,B) 

od X jest konstruktorem typu B.Jeśli masz dwa konstruktory o tej samej nazwie, nie możesz określić unikalnego typu dla funkcji, które z nich korzystają. Więc nie możesz podać dwóch konstruktorów w tym samym module o tej samej nazwie.

1

Nie możesz tego zrobić:

data A = C | Z 
data B = C 

data C = X | Y 

(Tak na marginesie, jeśli B jest identyczne do C, to dlaczego mają B w ogóle?)

ale co ty możliwe, zrób coś takiego:

data A = A_Other C | Z 
data B = B_Other C 

data C = X | Y 

Następnie można wzór mecz tak:

foo :: A -> String 
foo (A_Other X) = "X" 
foo (A_Other Y) = "Y" 
foo (  Z) = "Z" 

bar :: B -> String 
bar (B_Other X) = "X" 
bar (B_Other Y) = "Y" 

foobar :: C -> String 
foobar X = "X" 
foobar Y = "Y" 

Jeśli to ma sens ...

0

Nie możesz robić co chcesz, ponieważ są deklarowania wielu konstruktorów danych. W

data A = X | Y | Z 

jesteś rzeczywiście wprowadzenie typ A który ma 3 konstruktorów (wartości) X, Y i Z. Dlatego Twój pierwszy fragment kodu nie będzie się kompilował, widzi tę samą nazwę wymienioną jako konstruktory dla dwóch różnych typów! Jeśli można to zrobić trzeba by zadać sobie

X :: A 

lub

X :: B 

które w kontekście braku obiektowego jest przerażające! Musisz podać różne nazwy konstruktorów, aby udostępnić dane podstawowe, C.

Jeśli chcesz czynnik ten można zrobić jak inne posty sugerowali i uwzględnić-out dane w wyjątkowych konstruktorów dla każdego typu danych

data A = CForA C | Z 
data B = CForB C 

data C = X | Y 
Powiązane problemy