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 .)