Warum listet ghci desugar Listen und Typfamilien auf? Kann dies selektiv deaktiviert werden?

93

Ich versuche, die Ghci-Typanzeigen für meine Bibliotheken so intuitiv wie möglich zu gestalten, aber ich habe viele Schwierigkeiten, wenn ich erweiterte Typfunktionen verwende.

Angenommen, ich habe diesen Code in einer Datei:

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

import GHC.TypeLits

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

Ich lade es in ghci hoch und gebe dann den folgenden Befehl ein:

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

Leider sieht mir ghci ziemlich hässlich aus:

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

ghci hat den Zucker für Saiten auf Typebene entfernt. Gibt es eine Möglichkeit, ghci daran zu hindern, dies zu tun und mir nur die hübsche Version zu geben?


Nehmen wir an, ich erstelle eine ReplicateFunktion auf Typebene

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

Nun, wenn ich ghci nach einem Typ frage, der Folgendes verwendet LotsOfStrings:

ghci> :t undefined :: Container LotsOfStrings

ghci ist nett und gibt mir das hübsche Ergebnis:

undefined :: Container LotsOfStrings

Aber wenn ich nach der Replicated-Version frage ,

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

ghci ersetzt die Typfamilie, wenn dies für das Typensynonym nicht der Fall ist:

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

Warum ersetzt ghci die Typenfamilie, nicht aber das Typensynonym? Gibt es eine Möglichkeit zu kontrollieren, wann ghci die Substitution durchführen wird?

Mike Izbicki
quelle
7
Da Typensynonyme nur für den menschlichen Verzehr bestimmt sind, wird sie nicht ersetzt, da bestätigt wird, dass Sie das Typensynonym erstellt haben, weil Sie den Typ auf diese Weise schreiben / sehen wollten. Es wird durch die Typfamilie ersetzt, da es bei Typfamilien wirklich darum geht, einen Typ zu berechnen / abzuleiten, nicht ihn anzuzeigen.
AndrewC
Die Lösung für Ihr Problem liegt in Ihrer Frage - machen Sie ein Typensynonym, wenn Sie abkürzen möchten.
AndrewC
2
@AndrewC Ich habe mir gerade eine andere Frage zu Ihrem Kommentar ausgedacht: Warum werden die Arten von Strings manchmal als [Char]und manchmal als angezeigt String?
Mike Izbicki
1
Ich denke, ghci versucht, Typensynonyme beizubehalten, die es in der Quelle findet. Das heißt, wenn eine Funktion als vom Typ deklariert wirdString->String , wird der Typ ihres Ergebnisses als angezeigt String. Wenn es jedoch einen Typ aus Stücken konstruieren muss, wie z. B. in "abc"(was dasselbe ist wie 'a':'b':'c':[]), gibt es kein Synonym, das beibehalten werden muss. Das ist reine Spekulation.
n. 'Pronomen' m.
4
@nm: Beachten Sie, dass GHC einen ähnlichen Versuch unternimmt, die Namen von Typvariablen beizubehalten, wenn allgemeinere abgeleitete Typen mit weniger generischen, explizit benannten Typvariablen vereinigt werden. Ich vermute, dass wenn der explizite Typ Stringmit Typvariablen f aoder vereinheitlicht [a]wird, er [Char]aus ähnlichen Gründen wie später angezeigt wird .
CA McCann

Antworten:

2

Die Problemumgehung, die ich kenne, ist: kind. Zum Beispiel,

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

Gibt:

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

Während

ghci>: nett! (Container '[String, String, String, String, String])

Wird so etwas drucken:

Container

((':)

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

Offiziell stellen Sie ghci natürlich eine andere Frage kind, aber es funktioniert. Die Verwendung undefined ::ist sowieso eine Art Problemumgehung, daher dachte ich, dass dies ausreichen könnte.

user2141650
quelle
Ich habe nur verwendet undefined ::, um ein einfaches Beispiel zu geben. Das eigentliche Problem ist, wenn Sie eine Fehlermeldung erhalten, die einen Typ einer Liste von tausend verschiedenen Typen enthält. Es braucht Seiten, um es auszudrucken, und ist sehr schwer zu analysieren.
Mike Izbicki
Ja, fair genug. Könnte das realisiert haben. Ich schulde Ihnen eine bessere Antwort
user2141650
2

Dies ist in der kommenden GHC 7.8 behoben.

GHC 7.6 druckt Arten, wenn ein Datentyp PolyKinds verwendet. Sie sehen also (':) * String ('[] *)statt nur (':) String '[].

In GHC 7.8 werden Arten standardmäßig nicht mehr angezeigt und Ihr Datentyp wird wie erwartet als Liste gedruckt. Mit dem neuen Flag können -fprint-explicit-kindsSie explizite Arten wie in GHC 7.6 anzeigen. Ich kenne die Gründe dafür nicht. Vermutlich explizite Arten sollten dazu beitragen, PolyKinds zu verstehen.

sdcvvc
quelle
0
import GHC.TypeLits

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

Ich lade es in ghci hoch und gebe dann den folgenden Befehl ein:

:t undefined :: Container '[String,String,String,String,String]
ئیترده ڕۆم
quelle
So...? Sie erhalten immer noch das Ergebnis zurück, nehme ich an, dh String ((':) * String ((':) * String ((':) * ....
links um den