Добросовестная неоднозначность при использовании PolyKinds и семейств типов

У меня есть два семейства типов, одно из которых сопоставляет один тип другому типу другого типа и полиморфной функции:

{-# LANGUAGE PolyKinds, TypeFamilies, FlexibleContexts, ScopedTypeVariables #-} 

type family F (a :: k1) :: k2
type family G (a :: k2) :: *

f :: forall k1 k2 (a :: k1) (p :: k1 -> *) . p (a :: k1) -> G (F (a :: k1) :: k2)
f = undefined

Этот код не проверяет следующее сообщение об ошибке:

• Couldn't match type ‘G k20 (F k20 k1 a)’ with ‘G k2 (F k2 k1 a)’
  Expected type: p a -> G k2 (F k2 k1 a)
    Actual type: p a -> G k20 (F k20 k1 a)
  NB: ‘G’ is a non-injective type family

но я не могу понять, откуда возникла двусмысленность и как я могу указать недостающие виды?

Когда я использую только одно семейство типов, это работает:

g :: forall k1 k2 (a :: k1) (p :: k1 -> *) (q :: k2 -> *). p (a :: k1) -> q (F (a :: k1) :: k2)
g = undefined

2 ответа

Решение
f :: forall k1 k2 (a :: k1) (p :: k1 -> *). p a -> G (F a :: k2)

Позвольте мне попытаться сказать:

x :: [String]
x = f (Just 'a')

Это идет и создает экземпляры f с k1 ~ Type, a ~ Char, а также p ~ Maybe

f :: forall k2. Maybe Char -> G (F Char :: k2)

Что теперь? Ну мне дальше нужно G (F Char :: k2) ~ [String], но G это неинъективное семейство типов, поэтому нельзя сказать, какой из его аргументовk2 а также F Char :: k2-должно быть. Следовательно, определение x в ошибке; k2 является неоднозначным, и невозможно сделать заключение для него.

Тем не менее, вы можете довольно четко увидеть, что не использовать f когда-нибудь сможет сделать вывод k2, Причина в том, что k2 появляется только в типе f под неинъективным заявлением семейного типа (другая "плохая позиция" - это LHS =>). Он никогда не появляется в положении, в котором он может быть выведен. Поэтому без расширения вроде TypeApplications, f бесполезен, и никогда не может быть упомянут без появления этой ошибки. GHC дает обнаруживает это и вызывает ошибку при определении, а не использования. Появляющееся сообщение об ошибке примерно такое же, что и при попытке:

f0 :: forall k10 k20 (a :: k10) (p0 :: k10 -> *). p0 a0 -> G (F a0 :: k20)
f0 = f

Это приводит к тому же несоответствию типов, так как k20 из f0 должен соответствовать k2 из f1,

Вы можете заставить замолчать ошибку в определении f путем включения AllowAmbiguousTypes, который отключает эту проверку бесполезности во всех определениях. Однако, в одиночку, это просто выдвигает ошибку при каждом использовании f, Для того, чтобы на самом деле позвонить f, вы должны включить TypeApplications:

f0 :: forall k10 k20 (a :: k10) (p0 :: k10 -> *). p0 a0 -> G (F a0 :: k20)
f0 = f @k10 @k20 @a0 @p0

Альтернатива TypeApplications это что-то вроде Data.Proxy.Proxy, но это в значительной степени устарело, за исключением случаев более высокого ранга. (И даже тогда, это будет действительно без работы, когда у нас будет что-то вроде type-lambdas.)

Изначально проверка неоднозначности была предназначена для отклонения функций, которые никогда не могут быть вызваны из-за параметров типа и ограничений, которые не могут быть выведены из явных аргументов функции.

Однако в GHC 8.6.x таких функций нет, потому что все можно сделать явным TypeApplications, Я рекомендую просто включить AllowAmbiguousTypes а также TypeApplications, Предупреждение GHC о неоднозначных типах само по себе не очень информативно, поскольку оно отклоняет многие из допустимых вариантов использования приложений типов.

Другие вопросы по тегам