Pourquoi ghci desugar type des listes et des familles de types? Cela peut-il être désactivé de manière sélective?

93

J'essaie de rendre les types d'affichage ghci pour mes bibliothèques aussi intuitifs que possible, mais je rencontre beaucoup de difficultés lors de l'utilisation de fonctionnalités de type plus avancées.

Disons que j'ai ce code dans un fichier:

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

import GHC.TypeLits

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

Je le charge dans ghci, puis je tape la commande suivante:

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

Malheureusement, ghci me donne un aspect plutôt laid:

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

ghci a supprimé le sucre pour les chaînes de niveau de type. Y a-t-il un moyen d'empêcher ghci de faire cela et de me donner juste la jolie version?


Sur une note connexe, disons que je crée une Replicatefonction de niveau de type

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

Maintenant, quand je demande à ghci un type en utilisant LotsOfStrings:

ghci> :t undefined :: Container LotsOfStrings

ghci est sympa et me donne le joli résultat:

undefined :: Container LotsOfStrings

Mais si je demande la Replicateversion d,

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

ghci remplace la famille de types alors qu'il ne l'a pas fait pour le synonyme de type:

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

Pourquoi ghci fait-il la substitution pour la famille de types, mais pas pour le synonyme de types? Existe-t-il un moyen de contrôler quand ghci effectuera la substitution?

Mike Izbicki
la source
7
Parce que les synonymes de type sont conçus uniquement pour la consommation humaine - il n'effectue pas la substitution car il reconnaît que vous avez créé le synonyme de type parce que vous vouliez écrire / voir le type de cette façon. Il effectue la substitution avec la famille de types car les familles de types consistent en réalité à calculer / déduire un type, pas à l'afficher.
AndrewC
La solution à votre problème est dans votre question - créez un synonyme de type si vous souhaitez abréger.
AndrewC
2
@AndrewC J'ai juste pensé à une autre question liée à votre commentaire: Pourquoi les types de chaînes sont-ils parfois affichés comme [Char]et parfois affichés comme String?
Mike Izbicki
1
Je pense que ghci essaie de préserver les synonymes de type qu'il trouve dans la source. Autrement dit, si une fonction est déclarée comme étant de type String->String, le type de son résultat sera affiché sous la forme String. Cependant, s'il doit construire un type à partir de morceaux, comme dans eg "abc"(qui est le même que 'a':'b':'c':[]), il n'y a pas de synonyme à conserver. C'est de la pure spéculation.
n. «pronoms» m.
4
@nm: notez que GHC fait une tentative similaire pour conserver les noms des variables de type, lorsque des types inférés plus génériques s'unifient avec des variables de type moins génériques et nommées explicitement. Je soupçonne que si le type explicite Stringest unifié avec des variables de type f aou [a], il sera affiché comme [Char]après pour des raisons similaires.
CA McCann

Réponses:

2

La solution de contournement que je connais utilise: kind. Par exemple,

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

Donne:

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

Tandis que

ghci>: gentil! (Conteneur '[String, String, String, String, String])

Imprime quelque chose comme ceci:

Récipient

((':)

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

Officiellement, bien sûr, vous posez une question différente à ghci avec kind, mais cela fonctionne. L'utilisation undefined ::est en quelque sorte une solution de contournement, alors j'ai pensé que cela pourrait suffire.

user2141650
la source
Je n'utilisais que undefined ::pour donner un exemple simple. Le vrai problème est lorsque vous obtenez un message d'erreur qui a un type d'une liste de mille types différents. Il faut des pages pour l'imprimer et est très difficile à analyser.
Mike Izbicki
Ouais, assez bien. Aurait pu s'en rendre compte. Je vous dois une meilleure réponse
user2141650
2

Ce problème est résolu dans le prochain GHC 7.8.

GHC 7.6 imprime des types si un type de données utilise PolyKinds. Alors tu vois (':) * String ('[] *)au lieu de juste(':) String '[] .

Dans GHC 7.8, les genres ne sont plus affichés par défaut et votre type de données est plutôt imprimé sous forme de liste, comme vous vous en doutez. Vous pouvez utiliser le nouvel indicateur -fprint-explicit-kindspour voir les types explicites comme dans GHC 7.6. Je ne connais pas les raisons de cela, les types vraisemblablement explicites étaient censés être une aide à la compréhension de PolyKinds.

sdcvvc
la source
0
import GHC.TypeLits

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

Je le charge dans ghci, puis je tape la commande suivante:

:t undefined :: Container '[String,String,String,String,String]
ئیترده ڕۆم
la source
Alors...? Vous obtenez toujours le résultat désucré je suppose, c'est à dire String ((':) * String ((':) * String ((':) * ....
gauche vers