2015-11-17 32 views
7

Zastanawiam się dlaczego Rozpakowanych typy w Haskell mają te ograniczenia:Ograniczenia odpakowanych typów

  1. nie można zdefiniować do newtype dla rozpakowanych typu:

    newtype Vec = Vec (# Float#, Float# #) 
    

    ale można określić typ synonim:

    type Vec = (# Float#, Float# #) 
    
  2. Rodzaje rodzin nie mogą wrócić z typem skrzynki pocztowej:

    type family Unbox (a :: *) :: # where 
        Unbox Int = Int# 
        Unbox Word = Word# 
        Unbox Float = Float# 
        Unbox Double = Double# 
        Unbox Char = Char# 
    

Czy istnieją pewne podstawowe przyczyny tego, czy jest to tylko dlatego, że nikt nie poprosił o te cechy?

Odpowiedz

7

Parametryczny polimorfizm w Haskell polega na tym, że wszystkie wartości typów t :: * są równomiernie reprezentowane jako wskaźnik do obiektu wykonawczego. Tak więc ten sam kod maszynowy działa dla wszystkich wystąpień wartości polimorficznych.

Kontrastowe funkcje polimorficzne w Rust lub C++. Na przykład, funkcja identyfikacji nadal ma typ analogiczny do forall a. a -> a, ale ponieważ wartości różnych typów a mogą mieć różne rozmiary, kompilatory muszą generować różne kody dla każdej instatiacji. Oznacza to również, że nie możemy przekazać funkcje polimorficzne wokół w pudełkach Czas:

data Id = Id (forall a. a -> a) 

ponieważ taka funkcja musiałaby działać poprawnie dla dowolnych rozmiarów obiektów. Aby ta funkcja była dostępna, wymagana jest dodatkowa infrastruktura, na przykład możemy wymagać, aby funkcja wykonawcza forall a. a -> a pobierała dodatkowe niejawne argumenty, które przenoszą informacje o rozmiarze i konstruktorach/destruktorach wartości a.

Teraz problem z newtype Vec = Vec (# Float#, Float# #) jest to, że mimo Vec ma rodzaj *, kodu wykonawczego, który oczekuje wartości niektórych t :: * nie może poradzić. Jest to pula przydzielona do pary, a nie wskaźnik do obiektu Haskella, a przekazanie jej do kodu spodziewającego się obiektów Haskella może spowodować błędy w postaci segmentów lub błędów.

Ogólnie rzecz biorąc (# a, b #) nie jest koniecznie wielkością pointeru, więc nie możemy go skopiować do pól danych o rozmiarach pointeru.

Typy rodzin zwracających # typy są niedozwolone z przyczyn pokrewnych. Rozważmy następujący:

type family Foo (a :: *) :: # where 
    Foo Int = Int# 
    Foo a = (# Int#, Int# #) 

data Box = forall (a :: *). Box (Foo a) 

Nasz Box nie jest reprezentowalna wykonawcze, ponieważ Foo a ma różne rozmiary dla różnych a -s. Ogólnie, polimorfizm ponad # wymagałby generowania różnych kodów dla różnych wystąpień, jak w Rust, ale to źle wpływa na regularny polimorfizm parametryczny i sprawia, że ​​odtwarzanie wartości polimorficznych jest trudne, więc GHC nie przejmuje się tym.

(nie mówiąc jednak, że użyteczny realizacja nie mógłby zostać opracowany)

+1

"Problem z' nowym typem Vec = Vec (# Float #, Float # #) 'jest taki, że' Vec' ma rodzaj '*' "Ale dlaczego? Dlaczego musi mieć rodzaj "*"? Kiedy piszę 'type Vec = (# Float #, Float # #)' Vec ma rodzaj '# ', więc spodziewam się, że newty będzie miał również rodzaj" # ". –

+1

'typ Vec = ...' jest tylko synonimem. 'newtype' definiuje, no cóż, nowy typ, który różni się od typu wartości opakowanej. Zdarza się tak, że wszystkie konstrukty Haskella do definiowania nowych typów zwracają się w rodzaju "*". 'newtype's dla typów unboxed wymagałoby dużej części infrastruktury, o której wspomniałem powyżej, dla umożliwienia polimorfizmu obiektów o różnej wielkości, lub byłoby monomorficzne tylko w użyciu, co uczyniłoby je nieszczególnie lepszymi w porównaniu do używania tylko typu owiniętego . –

+0

@ AndrásKovács Może to być wykonane z rodzaju '# ', ale np. 'id :: a -> a' nie może być wyspecjalizowane w' a ~ Vec' od 'a :: *'. To ograniczenie jest związane z faktem, że środowisko wykonawcze obsługuje dane w ramkach jednolicie (ze wskaźnikami), gdy chcesz przekazać surowe dane unboxed, więc potrzebujesz niestandardowego identyfikatora dla każdego typu bez opakowania. Nie różni się to zbytnio od niezdolności Javy do posiadania generycznych, takich jak 'T ' ponieważ 'int' nie jest typem odniesienia. – chi

4

newtype że pozwalają na określenie przykłady klas

instance C Vec where ... 

, które nie mogą być określone za odpakowanych krotki. Zamiast tego wpisz synonimy, nie oferuj takiej funkcji.

Również, Vec nie będzie typu pudełkowego. Oznacza to, że nie można już tworzyć instancji zmiennych typu z ogólnie Vec, chyba że zezwala na to ich rodzaj. Na przykład [Vec] powinno być zabronione. Kompilator powinien w jakiś sposób śledzić "regularne" nowe typy i "unboxed" nowe typy. Będzie to, jak sądzę, jedyna korzyść z umożliwienia konstruktorowi danych Vec zawijania niepakowanych wartości w czasie kompilacji (ponieważ jest on usuwany w czasie wykonywania). Prawdopodobnie nie byłoby to wystarczające, by uzasadnić wprowadzenie niezbędnych zmian w silniku inferencyjnym.

+1

Och, powinna ona już mój 3 pytanie: dlaczego nie można zdefiniować klasę ponad odpakowanych typów tak: 'klasa C (u :: #) gdzie ... '? –

+0

"Vec nie byłby typem pudełkowym" Dlaczego tak musi być? Wpisz synonim 'type Vec = (# Float #, Float # #)' ma rodzaj '#' dlaczego 'newtype' nie ma? –

+0

"Kompilator powinien w jakiś sposób śledzić" regularne "nowe typy i" nowe "typy." Czy regularne sprawdzanie rodzaju nie wystarczy? –