Dlaczego lista typów i rodziny typów ghci desugar? Czy można to selektywnie wyłączyć?


93

Staram się, aby typy ghci wyświetlane w moich bibliotekach były jak najbardziej intuicyjne, ale napotykam wiele trudności podczas korzystania z bardziej zaawansowanych funkcji czcionek.

Powiedzmy, że mam ten kod w pliku:

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}

import GHC.TypeLits

data Container (xs::[*]) = Container

Ładuję go w ghci, a następnie wpisuję następujące polecenie:

ghci> :t undefined :: Container '[String,String,String,String,String]

Niestety ghci daje mi raczej brzydki wygląd:

:: Container
       ((':)
          *
          String
          ((':)
             * String ((':) * String ((':) * String ((':) * String ('[] *))))))

ghci usunął cukier dla stringów poziomu typu. Czy jest jakiś sposób, aby powstrzymać ghci przed zrobieniem tego i podaniem mi tylko ładnej wersji?


A propos, powiedzmy, że tworzę Replicatefunkcję na poziomie typu

data Nat1 = Zero | Succ Nat1

type family Replicate (n::Nat1) x :: [*]
type instance Replicate Zero x = '[]
type instance Replicate (Succ n) x = x ': (Replicate n x)

type LotsOfStrings = Replicate (Succ (Succ (Succ (Succ (Succ Zero))))) String

Teraz, kiedy pytam ghci o typ przy użyciu LotsOfStrings:

ghci> :t undefined :: Container LotsOfStrings

ghci jest fajny i daje mi ładny wynik:

undefined :: Container LotsOfStrings

Ale jeśli poproszę o Replicatewersję d,

ghci> :t undefined :: Container (Replicate (Succ (Succ (Succ (Succ (Succ Zero))))) String)

ghci zastępuje rodzinę typów, gdy nie zrobił tego dla synonimu typu:

:: Container
       ((':)
          *
          [Char]
          ((':)
             * [Char] ((':) * [Char] ((':) * [Char] ((':) * [Char] ('[] *))))))

Dlaczego ghci zastępuje rodzinę typów, a nie synonim typu? Czy istnieje sposób kontrolowania, kiedy ghci dokona zamiany?


7
Ponieważ synonimy typów są przeznaczone wyłącznie do spożycia przez ludzi - nie powoduje zastąpienia, ponieważ potwierdza, że ​​utworzyłeś synonim typu, ponieważ chciałeś zapisać / zobaczyć typ w ten sposób. Powoduje podstawienie z rodziną typów, ponieważ rodziny typów tak naprawdę polegają na obliczaniu / dedukowaniu typu, a nie na jego wyświetlaniu.
AndrewC

Rozwiązanie problemu tkwi w pytaniu - utwórz synonim typu, jeśli chcesz użyć skrótu.
AndrewC

2
@AndrewC Właśnie wymyśliłem kolejne pytanie związane z twoim komentarzem: Dlaczego typy ciągów znaków czasami są wyświetlane jako, [Char]a czasami są wyświetlane jako String?
Mike Izbicki

1
Myślę, że ghci próbuje zachować synonimy typu znalezione w źródle. Oznacza to, że jeśli funkcja jest zadeklarowana jako typu String->String, typ jej wyniku zostanie wyświetlony jako String. Jeśli jednak ma skonstruować typ z kawałków, jak np. "abc"(Co jest tym samym 'a':'b':'c':[]), nie ma synonimu do zachowania. To czysta spekulacja.
n. zaimki m.

4
@nm: Należy zauważyć, że GHC podejmuje podobną próbę zachowania nazw zmiennych typu, gdy bardziej ogólne typy wywnioskowane są ujednolicone z mniej ogólnymi, jawnie nazwanymi zmiennymi typu. Podejrzewam, że jeśli jawny typ Stringjest zunifikowany ze zmiennymi typu f alub [a]zostanie wyświetlony [Char]później z podobnych powodów.
CA McCann

Odpowiedzi:


2

Znane mi obejście to: rodzaj. Na przykład,

ghci>: kind (Container '[String, String, String, String, String])

Daje:

(Container '[String, String, String, String, String]) :: *

Podczas

ghci>: kind! (Kontener „[ciąg, ciąg, ciąg, ciąg, ciąg])

Wydrukuje coś takiego:

Pojemnik

((':)

  *
  [Char]
  ((':)
     * [Char] ((':) * [Char] ((':) * [Char] ((':) * [Char] ('[] *))))))

Oficjalnie, oczywiście, zadajesz ghci inne pytanie kind, ale to działa. Używanie undefined ::jest i tak swego rodzaju obejściem, więc pomyślałem, że to może wystarczyć.


Użyłem tylko undefined ::prostego przykładu. Prawdziwy problem polega na tym, że pojawia się komunikat o błędzie zawierający typ listy tysięcy różnych typów. Wydrukowanie zajmuje strony i jest bardzo trudne do przeanalizowania.
Mike Izbicki

Tak, w porządku. Mogłem to sobie uświadomić. Jestem ci winien lepszą odpowiedź
user2141650

2

Zostało to naprawione w nadchodzącym GHC 7.8.

GHC 7.6 wypisuje rodzaje, jeśli typ danych używa PolyKinds. Więc widzisz (':) * String ('[] *)zamiast tylko (':) String '[].

W GHC 7.8 rodzaje nie są już domyślnie wyświetlane, a typ danych jest ładnie wydrukowany jako lista, jak można się spodziewać. Możesz użyć nowej flagi, -fprint-explicit-kindsaby zobaczyć jawne rodzaje, jak w GHC 7.6. Nie znam powodów, przypuszczalnie wyraźne rodzaje miały być pomocą w zrozumieniu PolyKinds.


0
import GHC.TypeLits

data Container (xs::[*]) = Container

Ładuję go w ghci, a następnie wpisuję następujące polecenie:

:t undefined :: Container '[String,String,String,String,String]

Więc...? Przypuszczam, że nadal otrzymujesz wynik bez cukru, tj String ((':) * String ((':) * String ((':) * ....
leftaround około
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.