Я намагаюся зробити типи 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 зробить заміну?
[Char]
а іноді відображаються як String
?
String->String
, то тип її результату відображатиметься як String
. Однак якщо він повинен побудувати тип із фрагментів, як, наприклад, наприклад "abc"
(який такий самий, як 'a':'b':'c':[]
), немає синоніму для збереження. Це чисті спекуляції.
String
уніфікований зі змінними типу f a
або [a]
, він буде відображатися як [Char]
після з подібних причин.