2013-01-09 12 views
11

rozważyć następująceJaka jest składnia współproduktu (rozłącznego połączenia) typów w Haskell?

data Point=Point{x::Float,y::Float} 
data Shape=Circle{centre::Point,radius::Float} 
      |Rectangle {uleft::Point,bRight::Point} 

Tutaj typ kształt jest współprodukt dwóch rodzajów Circle i Rectangle. Mogę ponownie użyć typów Circle i Rectangle w innym miejscu. Więc byłoby to przydatne zamiast tego:

data Point=Point{x::Float,y::Float} 
data Circle=Circle{centre::Point,radius::Float} 
data Rectangle=Rectangle {uleft::Point,bRight::Point} 
data Shape =Circle | Rectangle 

ale pojawia się błąd kompilacji, gdy robię to: okrąg jest zadeklarowany dwa razy. Jaka jest prawidłowa składnia przy próbie tego, lub nie jest to możliwe?

+0

Spójrz: http://calculist.blogspot.com.au/2008/02/true-unions.html – CMCDragonkai

Odpowiedz

14

współproduktu typów w Haskell jest powszechnie oznaczane Either:

data Either a b = Left a | Right b 

type Shape = Either Circle Rectangle 
-- so you have shapes as either Left c for some Circle c 
-- or Right r for some Rectangle r 

Działa to całkiem ładnie, choć z przyczyn technicznych it isn't exactly a coproduct. Innym powszechnym sposobem byłoby zdefiniować typ tak:

data Shape = CircleShape Circle | RectangleShape Rectangle 

tak że CircleShape :: Circle -> Shape i RectangleShape :: Rectangle -> Shape macie dwa zastrzyki.

Błędem jest mówić tak jak w pytaniu, że oryginalny Shape jest współproduktem typów Circle i Rectangle, ponieważ te dwa ostatnie nie są typami. Jeśli chcesz ustawić rzeczy tak, aby Circle p r było zarówno wartością typu Circle, jak i wartością typu Shape, to jest to naprawdę sprzeczne z duchem systemu typu Haskella (chociaż coś podobnego może być możliwe w przypadku wystarczająco wielu rozszerzeń systemu typu).

+0

Są konstruktorami wartości, prawda? Podoba mi się twoje pierwsze rozwiązanie, ale nie wydaje się łatwe do rozszerzenia, to jest dla współpowodu trzech z czterech typów. –

+1

Tak, konstruktorów wartości. Drugie rozwiązanie jest łatwiejsze do rozszerzenia, ponieważ możesz po prostu dodać konstruktorów (być może uda im się wymyślić dla nich lepsze nazwy) i jest to prawie ten sam pomysł. –

9

Nie jest to bezpośrednio możliwe, ale masz kilka opcji. W tym przypadku, chciałbym iść z GADT indeksowane przez DataKind:

{-# LANGUAGE DataKinds, GADTs, KindSignatures #-} 

data ShapeType = Circle | Rectangle 

data Shape :: ShapeType -> * where 
    CircleShape :: { centre :: Point, radius :: Float } -> Shape Circle 
    RectangleShape { uleft :: Point, bRight :: Point } -> Shape Rectangle 

Potem, kiedy tylko wan do czynienia z kształtami w ogóle, po prostu użyć Shape a, a jeśli chcesz prostokąt lub okrąg konkretnie, używasz odpowiednio Shape Rectangle lub Shape Circle.

Powiązane problemy