Вывод типа GADT с типами высшего рода
У меня есть код, который компилируется:
{-# LANGUAGE ScopedTypeVariables, KindSignatures, GADTs,
FlexibleContexts #-}
module Foo where
data Foo :: (* -> *) where
Foo :: c m zp' -> Foo (c m zp)
f :: forall c m zp d . Foo (c m zp) -> d
f y@(Foo (x :: c m a)) = g x y
g :: c m a -> Foo (c m b) -> d
g = error ""
Главное, что мне нужно в моем реальном коде, это убедить GHC, что если y
имеет тип Foo (c m zp)
а также x
имеет тип c' m' zp'
, затем c' ~ c
а также m' ~ m
, Приведенный выше код достигает этого, потому что я могу позвонить g
,
Я хочу изменить этот код двумя ортогональными способами, но я не могу понять, как заставить GHC скомпилировать код с любым изменением.
Первое изменение: Добавить -XPolyKinds
, GHC 7.8.3 жалуется:
Foo.hs:10:11:
Could not deduce ((~) (k2 -> k3 -> *) c1 c)
from the context ((~) * (c m zp) (c1 m1 zp1))
bound by a pattern with constructor
Foo :: forall (k :: BOX)
(k :: BOX)
(c :: k -> k -> *)
(m :: k)
(zp' :: k)
(zp :: k).
c m zp' -> Foo (c m zp),
in an equation for ‘f’
at Foo.hs:10:6-21
‘c1’ is a rigid type variable bound by
a pattern with constructor
Foo :: forall (k :: BOX)
(k :: BOX)
(c :: k -> k -> *)
(m :: k)
(zp' :: k)
(zp :: k).
c m zp' -> Foo (c m zp),
in an equation for ‘f’
at Foo.hs:10:6
‘c’ is a rigid type variable bound by
the type signature for f :: Foo (c m zp) -> d
at Foo.hs:9:13
Expected type: c1 m1 zp'
Actual type: c m a
Relevant bindings include
y :: Foo (c m zp) (bound at Foo.hs:10:3)
f :: Foo (c m zp) -> d (bound at Foo.hs:10:1)
In the pattern: x :: c m a
In the pattern: Foo (x :: c m a)
In an equation for ‘f’: f y@(Foo (x :: c m a)) = g x y
Foo.hs:10:11:
Could not deduce ((~) k2 m1 m)
from the context ((~) * (c m zp) (c1 m1 zp1))
...
Второе изменение: забыть о -XPolyKinds
, Вместо этого я хочу использовать -XDataKinds
создать новый вид и ограничить вид m
:
{-# LANGUAGE ScopedTypeVariables, KindSignatures, GADTs,
FlexibleContexts, DataKinds #-}
module Foo where
data Bar
data Foo :: (* -> *) where
Foo :: c (m :: Bar) zp' -> Foo (c m zp)
f :: forall c m zp d . Foo (c m zp) -> d
f y@(Foo (x :: c m a)) = g x y
g :: c m a -> Foo (c m b) -> d
g = error ""
Я получаю похожие ошибки (can't deduce (c1 ~ c)
, can't deduce (m1 ~ m)
).DataKinds
здесь уместно: если я ограничу m
иметь вид Constraint
вместо доброго Bar
код компилируется нормально.
Я привел два примера того, как сломать исходный код, оба из которых используют типы с более высоким родом. Я пытался использовать case case вместо шаблонных охранников, я пытался дать тип node
вместо x
Мои обычные трюки здесь не работают.
Я не требователен, где тип для x
в конечном итоге / как это выглядит, я просто должен быть в состоянии убедить GHC, что если y
имеет тип Foo (c m zp)
, затем x
имеет тип c m zp'
для некоторого несвязанного типа zp'
,
2 ответа
Я значительно упростил исходный вопрос до следующего, который компилируется без {-# LANGUAGE PolyKinds #-}
но не компилируется с PolyKinds
,
{-# LANGUAGE ScopedTypeVariables, KindSignatures, GADTs #-}
{-# LANGUAGE PolyKinds #-}
data Pair1 :: (* -> *) where
Pair1 :: Pair1 (c a, c b)
data D p a where
D :: p (a, b) -> D p a -> D p b
f :: forall c z. D Pair1 (c z) -> D Pair1 (c z)
f y@(D Pair1 x) |
(_ :: D Pair1 (c z)) <- y,
(_ :: D Pair1 (c z')) <- x = y
С PolyKinds
включил ошибку компилятора
Could not deduce (c1 ~ c)
from the context ((a, c z) ~ (c1 a1, c1 b))
Эта ошибка говорит о том, что я уже подозревал, что ответ зависит от того, является ли приложение многоугольного типа инъективным. Если бы приложение типа polykinded было инъективным, мы могли бы вывести c1 ~ c
следующее.
(a, c z) ~ (c1 a1, c1 b)
(a,) (c z) ~ (c1 a1,) (c1 b) {- switch to prefix notation -}
c z ~ c1 b {- f a ~ g b implies a ~ b -}
c ~ c1 {- f a ~ g b implies f ~ g -}
c1 ~ c {- ~ is reflexive -}
Приложение типа Polykinded является инъективным, но ghc этого не знает. Чтобы ghc мог сделать вывод, что приложение типа является инъективным, нам нужно предоставить сигнатуры вида, чтобы компилятор знал, что типы эквивалентны.
Я не нашел достаточно добрых аннотаций для вашей оригинальной, упрощенной версии проблемы. При упрощении проблем с жонглированием типов, сокращение типа до Proxy
иногда чрезмерно, поскольку оставляет меньше мест для прикрепления сигнатур типов. Вы нашли места для прикрепления добрых подписей к более значимой проблеме.
Проблема может быть решена путем добавления добрых подписей.
Например, при использовании -XPolyKinds
, следующий код компилируется:
{-# LANGUAGE ScopedTypeVariables, KindSignatures, GADTs,
FlexibleContexts, PolyKinds #-}
module Foo where
data Foo :: (* -> *) where
Foo :: (c :: k -> * -> *) m zp' -> Foo (c m zp)
f :: forall (c :: k -> * -> *) m zp d . Foo (c m zp) -> d
f y@(Foo x) = g x y
g :: c m a -> Foo (c m b) -> d
g = error ""
Для -XDataKinds
версия, мне тоже нужна добрая подпись на g
:
{-# LANGUAGE ScopedTypeVariables, KindSignatures, GADTs,
FlexibleContexts, DataKinds #-}
module Foo where
data Bar
data Foo :: (* -> *) where
Foo :: (c :: Bar -> * -> *) m zp' -> Foo (c m zp)
f :: forall (c :: Bar -> * -> *) m zp d . Foo (c m zp) -> d
f y@(Foo x) = g x y
g :: forall (c :: Bar -> * -> *) m a b d . c m a -> Foo (c m b) -> d
g = error ""
Не уверен, почему мне нужно больше сигналов для DataKinds
и это немного раздражает, когда приходится копировать их повсюду, но это делает работу.