2013-01-09 8 views
11

considerare il seguenteQual'è la sintassi per il coprodotto (unione disgiunta) dei tipi in Haskell?

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

Qui la forma tipo è un coprodotto di due tipi cerchio e rettangolo. Potrei voler riutilizzare i tipi Circle and Rectangle altrove. Quindi sarebbe utile fare questo, invece:

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 

ma ottengo un errore di compilazione quando faccio questo: Circle viene dichiarato due volte. Qual è la sintassi corretta per tentare questo, o questo non è possibile?

+0

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

risposta

14

La coprodotto di tipi in Haskell è comunemente indicato con 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 

Questo funziona abbastanza bene, anche se per motivi tecnici it isn't exactly a coproduct. Un altro modo comune sarebbe quello di definire un tipo in questo modo:

data Shape = CircleShape Circle | RectangleShape Rectangle 

in modo che CircleShape :: Circle -> Shape e RectangleShape :: Rectangle -> Shape sono le tue due iniezioni.

È sbagliato dire come nella domanda che l'originale Shape è un coprodotto di tipi Circle e Rectangle, perché gli ultimi due non sono tipi. Se si desidera impostare le cose in modo che Circle p r sia un valore di tipo Circle e un valore di tipo Shape, allora questo è davvero contrario allo spirito del sistema di tipi Haskell (anche se qualcosa di simile potrebbe essere possibile con un numero sufficiente di estensioni di sistema di tipo).

+0

Sono costruttori di valore, giusto? Mi piace la tua prima soluzione ma non sembra facilmente estendibile, cioè per il coprodotto di tre o quattro tipi. –

+1

Sì, costruttori di valori. La seconda soluzione è più facilmente estendibile, dal momento che puoi semplicemente aggiungere costruttori (potrebbe essere in grado di trovare anche nomi migliori per loro) ed è praticamente la stessa idea. –

9

Questo non è direttamente possibile, ma hai alcune opzioni. In questo caso, vorrei andare con un GADT indicizzati da un 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 

Poi, ogni volta che si wan a che fare con le forme, in generale, è sufficiente utilizzare Shape a, e se volete un rettangolo o un cerchio specifico, usi Shape Rectangle o Shape Circle, rispettivamente.

Problemi correlati