Мы можем написать отдельные комплексные экземпляры для параметров типа *
:
class MyClass d where
f :: d -> Int
instance MyClass (Maybe d) where
f _ = 3
test1 :: Maybe d -> Int
test1 x = f x
Это компилируется просто отлично, и обратите внимание, что нам НЕ нужно ограничение (MyClass (Maybe d)) для test1, потому что компилятор находит соответствующий экземпляр для любого Maybe d
.
Мы даже можем написать всеобъемлющий экземпляр для параметров класса, которые являются конструкторами типов:
class MyClass2 (a :: * -> *) where
g :: Tagged (a Int) Int
instance MyClass2 a where
g = Tagged 3
test2 :: Int
test2 = untag (g :: Tagged (Maybe Int) Int)
Это также отлично компилируется, и test2 не нуждается в ограничении (MyClass2 Maybe), потому что компилятор находит соответствующий экземпляр.
Рассмотрим следующий игрушечный код для вычисления длины списка (продвинутых) типов:
{-# LANGUAGE TypeOperators,
DataKinds,
KindSignatures,
GADTs,
FlexibleInstances,
FlexibleContexts,
ScopedTypeVariables #-}
import Data.Tagged
import Data.Proxy
data HList :: [*] -> * where
HNil :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
class ListLen a where
len :: Tagged a Int
instance ListLen (HList '[]) where
len = Tagged 0
instance (ListLen (HList as)) => ListLen (HList (a ': as)) where
len = Tagged (1+(untag (len :: Tagged (HList as) Int)))
test3 :: Int
test3 = untag (len :: Tagged (HList '[Int, Double, Integer]) Int)
test4 :: (Proxy (HList qs)) -> Int
test4 (_ :: Proxy (HList qs)) = untag (len :: Tagged (HList qs) Int) -- error occurs here
Это приводит к ошибке:
No instance for (ListLen (HList qs))
arising from a use of `len'
Possible fix: add an instance declaration for (ListLen (HList qs))
...
Если мы закомментируем подпись для test4
, GHCi выведет тип как
test4 :: (ListLen (HList qs)) => (Proxy (HList qs)) -> Int
но в этом нет необходимости. Ясно, что я написал экземпляры для ListLen
, которые соответствуют любому мыслимому списку типов: случай «пустой список» и случай «против». Проблема остается той же, если я изменю тип параметра ListLen
на вид [*]
(т. е. удалю оболочку HList
в ListLen
и его экземплярах).
Если мы закомментируем test4
, test3
компилируется и работает нормально: поскольку я (по сути) дал ему явный синтаксис ('[Int,Double,Integer]
) для того, как я построил список типов, компилятор смог найти соответствующие экземпляры.
Я пытаюсь написать код, который создает для меня список типов, поэтому мне не придется писать явный синтаксис списка типов. Однако кажется, что использование явного синтаксиса — единственный способ, с помощью которого GHC может сопоставить эти всеобъемлющие экземпляры. Может быть, я пропустил какое-то дело? Синтаксис, который я не использую?
Что я могу сделать, чтобы GHC понял, что у меня есть экземпляр для всего вида [*]
? Я использую GHC 7.4.2. Это может быть связано с предыдущим сообщением о деконструкции не продвигаемых типов. ДОКУМЕНТ о продвижении шрифтов можно найти здесь.