93
votes

I'm trying to make the types ghci displays for my libraries as intuitive as possible, but I'm running into a lot of difficulties when using more advanced type features.

Let's say I have this code in a file:

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

import GHC.TypeLits

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

I load it up in ghci, then I type the following command:

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

Unfortunately, ghci gives me the rather ugly looking:

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

ghci has removed the sugar for type level strings. Is there any way to prevent ghci from doing this and giving me just the pretty version?


On a related note, lets say I create a type level Replicate function

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

Now, when I ask ghci for a type using LotsOfStrings:

ghci> :t undefined :: Container LotsOfStrings

ghci is nice and gives me the pretty result:

undefined :: Container LotsOfStrings

But if I ask for the Replicated version,

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

ghci substitutes in for the type family when it didn't do that for the type synonym:

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

Why is ghci doing the substitution for the type family, but not the type synonym? Is there a way to control when ghci will do the substitution?

3
Because type synonyms are designed purely for human consumption - it doesn't make the substitution because it acknowledges that you made the type synonym because you wanted to write/see the type that way. It makes the substitution with the type family because type families are really about calculating/deducing a type, not displaying it.AndrewC
The solution to your problem is in your question - make a type synonym if you want to abbreviate.AndrewC
@AndrewC I just thought up another question related to your comment: Why do the types of Strings sometimes get displayed as [Char] and sometimes get displayed as String?Mike Izbicki
I think ghci tries to preserve type synonyms it finds in the source. That is, if a function is declared to be of type String->String, then the type of its result will be displayed as String. However if it has to construct a type from pieces, as in e.g. "abc" (which is the same as 'a':'b':'c':[]) there's no synonym to preserve. This is pure speculation.n. 1.8e9-where's-my-share m.
@n.m.: Note that GHC makes a similar attempt to preserve the names of type variables, when more generic inferred types unify with less generic, explicitly named type variables. I suspect that if the explicit type String is unified with type variables f a or [a], it will be displayed as [Char] afterwards for similar reasons.C. A. McCann

3 Answers

2
votes

The workaround that I know of is using :kind. For instance,

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

Gives:

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

While

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

Will print something like this:

Container

((':)

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

Officially, of course, you're asking ghci a different question with kind, but it works. Using undefined :: is sort of a workaround anyhow, so I thought this might suffice.

2
votes

This is fixed in upcoming GHC 7.8.

GHC 7.6 prints kinds if a datatype uses PolyKinds. So you see (':) * String ('[] *) instead of just (':) String '[].

In GHC 7.8, kinds are no longer shown by default and your datatype is pretty printed as a list, as you would expect. You can use the new flag -fprint-explicit-kinds to see explicit kinds as in GHC 7.6. I don't know the reasons for this, presumably explicit kinds were meant to be an aid for understanding PolyKinds.

0
votes
import GHC.TypeLits

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

I load it up in ghci, then I type the following command:

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