Почему ghci desugar использует списки типов и семейства типов? Можно ли это выборочно отключить?

93

Я пытаюсь сделать типы, отображаемые ghci для моих библиотек, максимально интуитивно понятными, но я сталкиваюсь с множеством трудностей при использовании более продвинутых функций типов.

Допустим, у меня есть этот код в файле:

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

import GHC.TypeLits

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

Я загружаю его в ghci, затем набираю следующую команду:

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

К сожалению, ghci дает мне довольно уродливый вид:

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

ghci удалил сахар для строк уровня типа. Есть ли способ помешать ghci сделать это и дать мне только красивую версию?


На соответствующую записку, позволяет сказать , что я создаю уровня типа Replicateфункции

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

Теперь, когда я прошу ghci указать тип, использующий LotsOfStrings:

ghci> :t undefined :: Container LotsOfStrings

ghci хорош и дает хороший результат:

undefined :: Container LotsOfStrings

Но если я попрошу Replicateверсию d,

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

ghci заменяет семейство типов, когда он этого не сделал для синонима типа:

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

Почему ghci выполняет замену для семейства типов, но не для синонима типа? Есть ли способ контролировать, когда ghci выполнит замену?

Майк Избицки
источник
7
Поскольку синонимы типов разработаны исключительно для потребления человеком - они не производят подстановки, потому что они подтверждают, что вы сделали синоним типа, потому что хотели записать / увидеть тип таким образом. Он выполняет замену семейством типов, потому что семейства типов на самом деле предназначены для вычисления / вывода типа, а не для его отображения.
AndrewC
Решение вашей проблемы в вашем вопросе - сделайте синоним типа, если вы хотите сократить.
AndrewC
2
@AndrewC Я только что придумал еще один вопрос, связанный с вашим комментарием: почему типы строк иногда отображаются как, [Char]а иногда отображаются как String?
Майк Избицки
1
Я думаю, что ghci пытается сохранить синонимы типов, найденные в исходном коде. То есть, если функция объявлена ​​как имеющая тип String->String, то тип ее результата будет отображаться как String. Однако, если он должен построить тип из частей, как, например, "abc"(что то же самое 'a':'b':'c':[]), нет синонима, который нужно сохранить. Это чистое предположение.
п. 'местоимения' м.
4
@nm: обратите внимание, что GHC делает аналогичную попытку сохранить имена переменных типа, когда более общие выведенные типы объединяются с менее универсальными, явно названными переменными типа. Я подозреваю, что если явный тип Stringобъединен с переменными типа f aили [a], он будет отображаться как [Char]впоследствии по аналогичным причинам.
CA McCann,

Ответы:

2

Обходной путь, о котором я знаю, использует: kind. Например,

ghci>: kind (Контейнер '[Строка, Строка, Строка, Строка, Строка])

Дает:

(Контейнер '[Строка, Строка, Строка, Строка, Строка]) :: *

Пока

ghci>: добрый! (Контейнер '[Строка, Строка, Строка, Строка, Строка])

Напечатает примерно так:

Контейнер

((':)

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

Официально, конечно, вы задаете ghci другой вопрос kind, но он работает. В undefined ::любом случае использование - это своего рода обходной путь, поэтому я подумал, что этого может хватить.

user2141650
источник
Я использовал только undefined ::простой пример. Настоящая проблема возникает, когда вы получаете сообщение об ошибке, в котором есть список из тысячи различных типов. Для его распечатки требуются страницы, и их очень трудно разобрать.
Майк Избицки
Да, достаточно честно. Мог бы это понять. Я должен вам лучший ответ
user2141650 08
2

Это исправлено в выпуске GHC 7.8.

GHC 7.6 печатает виды, если тип данных использует PolyKinds. Итак, вы видите, (':) * String ('[] *)а не просто (':) String '[].

В GHC 7.8 типы больше не отображаются по умолчанию, и ваш тип данных печатается в виде списка, как и следовало ожидать. Вы можете использовать новый флаг -fprint-explicit-kindsдля просмотра явных видов, как в GHC 7.6. Я не знаю причин для этого, предположительно явные типы должны были помочь в понимании PolyKinds.

sdcvvc
источник
0
import GHC.TypeLits

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

Я загружаю его в ghci, затем набираю следующую команду:

:t undefined :: Container '[String,String,String,String,String]
ئیترده ڕۆم
источник
Так...? Вы все еще получаете результат обратно обессахаренным я полагаю, то есть String ((':) * String ((':) * String ((':) * ....
leftaround примерно