2014-12-15 7 views
12

It's written, że krotki Haskella są po prostu inną składnią dla algebraicznych typów danych. Podobnie są przykłady jak przedefiniować konstruktory wartości za pomocą krotek.Jaka jest różnica między konstruktorami wartości a krotkami?

Na przykład typ danych Drzewo w Haskell może być zapisany jako

data Tree a = EmptyTree | Node a (Tree a) (Tree a) 

które mogą być przekształcone w „forma krotki” tak:

data Tree a = EmptyTree | Node (a, Tree a, Tree a) 

Jaka jest różnica między Node konstruktor wartości w pierwszym przykładzie, a rzeczywisty tuple w drugim przykładzie? tj. Node a (Tree a) (Tree a) vs. (a, Tree a, Tree a) (oprócz samej składni)?

Pod maską znajduje się Node a (Tree a) (Tree a) tylko inna składnia dla 3-krotnej liczby odpowiednich typów w każdej pozycji?

wiem, że można częściowo zastosować konstruktor wartości, takich jak Node 5 które będą miały typ: (Node 5) :: Num a => Tree a -> Tree a -> Tree a

możliwość sortowania można częściowo stosować zbyt krotki, używając (,,) jako funkcję ... ale to nie robi” t wiedzieć o potencjalnych typów dla wpisów un-bound, takich jak:

Prelude> :t (,,) 5 
(,,) 5 :: Num a => b -> c -> (a, b, c) 

chyba, myślę, jawnie zadeklarować typ z ::.

Oprócz specjałów składniowych takich jak ten, a także tego ostatniego przykładu określania typu, istnieje różnica w materiale między tym, czym jest "konstruktor wartości" w Haskell, a krotką używaną do przechowywania wartości pozycyjnych tego samego typu są argumenty konstruktora wartości?

Odpowiedz

16

Cóż, w zasadzie nie ma żadnej różnicy, a w rzeczywistości inne języki (OCaml, Elm) przedstawiają oznaczone związki w ten właśnie sposób - tagi na krotkach lub rekordach pierwszej klasy (których Haskell nie ma). Osobiście uważam, że jest to wada projektowa w Haskell.

Istnieją pewne praktyczne różnice jednak:

  1. Lenistwo. Krotki Haskella są leniwe i nie możesz tego zmienić. Można jednak zaznaczyć pola konstruktora jako surowe: ślad

    data Tree a = EmptyTree | Node !a !(Tree a) !(Tree a) 
    
  2. pamięci i wydajności. Ominięcie typów pośrednich zmniejsza ślad i podnosi wydajność. Możesz przeczytać więcej na ten temat w this fine answer.

    Możesz także oznaczyć surowe pola za pomocą the UNPACK pragma, aby jeszcze bardziej zmniejszyć ślad. Alternatywnie możesz użyć the -funbox-strict-fields compiler option. Jeśli chodzi o ostatnią, po prostu wolę mieć ją domyślnie we wszystkich moich projektach. Zobacz na przykład the Hasql's Cabal file.


Uwzględniając stwierdzono powyżej, jeśli jest to leniwy typ, który szukasz, a następnie następujące fragmenty powinny skompilować się do tego samego:

data Tree a = EmptyTree | Node a (Tree a) (Tree a) 

data Tree a = EmptyTree | Node {-# UNPACK #-} !(a, Tree a, Tree a) 

Więc myślę, że można powiedzmy, że można używać krotek do przechowywania leniwych pól konstruktora bez kary. Choć należy wspomnieć, że ten wzorzec jest dość niekonwencjonalny w społeczności Haskella.

Jeśli chodzi o ścisły typ i redukcję śladu, o które prosisz, to nie ma innego sposobu niż denormalizacja twoich krotek bezpośrednio w polach konstruktora.

+0

Na podstawie punktu 2 wydaje się, że wprowadzenie typu danych (w przeciwieństwie do funkcji, które działają poza konwencją krotki) dotyczy głównie semantyki wprowadzanej dla konsumentów modułu: sposobu organizowania myślenia i czytania kodu. Jeśli wydajność osiągnięta w tym przypadku nie jest duża (co, jak zakładam, jest prawie zawsze, biorąc pod uwagę wszechobecność tworzenia typów danych w Haskell), to dodatkowy zysk semantyczny wygrywa. Ale jeśli coś jest bardzo krytyczne dla wydajności lub jeśli jest to prywatna część modułu, którego niewielu klientów powinno potrzebować, dobrze jest pomylić po stronie konwencji tuple? – ely

+0

Kiedy mówię "konwencja krotkowa", mam na myśli funkcje przeznaczone do pracy na krotce, która nie jest inaczej nazwana lub używana w typie danych. Mówię to w ten sposób, ponieważ (i mógłbym być zdezorientowany) wygląda na to, że nie można mieć rekurencyjnego typu danych zrobionego wyłącznie z "krotki" bez użycia słów kluczowych "data" lub "newtype", które następnie trafiałyby te rozważania dotyczące pamięci, prawda? – ely

+0

'newtype' jest pojęciem kompilacji i zostaje usunięte podczas kompilacji. W przeciwieństwie do 'danych' nie wprowadza narzutów pamięci nad typem, który zawija. Aktualizacje mojej odpowiedzi powinny wyjaśnić resztę. –

11

Są one tak zwane izomorficzne, co oznacza "mieć ten sam kształt". Możesz napisać coś jak

data Option a = None | Some a 

I to jest izomorficzna

data Maybe a = Nothing | Just a 

co oznacza, że ​​można napisać dwie funkcje

f :: Maybe a -> Option a 
g :: Option a -> Maybe a 

f . g == id == g . f taka, że ​​dla wszystkich możliwych wejść. Możemy zatem powiedzieć, że (,,) jest konstruktorem danych izomorficzna do konstruktora

data Triple a b c = Triple a b c 

Ponieważ można napisać

f :: (a, b, c) -> Triple a b c 
f (a, b, c) = Triple a b c 

g :: Triple a b c -> (a, b, c) 
g (Triple a b c) = (a, b, c) 

And Node jako konstruktor jest szczególnym przypadkiem Triple, mianowicie Triple a (Tree a) (Tree a). W rzeczywistości, można nawet iść tak daleko, aby powiedzieć, że definicja Tree można zapisać jako

newtype Tree' a = Tree' (Maybe (a, Tree' a, Tree' a)) 

newtype jest wymagana, ponieważ nie można mieć type alias być rekurencyjne. Wszystko, co musisz zrobić, to powiedzieć, że EmptyLeaf == Tree' Nothing i Node a l r = Tree' (Just (a, l, r)). Możesz po prostu napisać funkcje, które konwertują pomiędzy tymi dwoma.

Zauważ, że to wszystko z matematycznego punktu widzenia. Kompilator może dodać dodatkowe metadane i inne informacje, aby móc zidentyfikować konkretnego konstruktora, co spowoduje, że będą zachowywać się nieco inaczej w środowisku wykonawczym.

+0

Tak, nie mówię o matematycznym izomorfizmie. Interesuje mnie faktyczna reprezentacja w pamięci i czy istnieje istotna różnica. Można powiedzieć, że struktury C zaimplementowane za pomocą 'Py_Object' są rzekomo izomorficzne dla klas Pythona, ale wyraźnie widać różnicę między pisaniem własnych typów C a używaniem klasy Python' class' lub 'type'. – ely

+0

@ prpl.mnky.dshwshr Następnie odpowiedź Nikity jest bardziej tym, czego szukasz. – bheklilr

+0

Tak, ale ten również dobrze jest zachować, na wypadek, gdyby mniej osób z matematyką zastanawiało się nad tym samym pytaniem. – ely

Powiązane problemy