Семейства типов и количественные ограничения не смешиваются. Тем более с равенствами типов . Но есть способ разделить их для достижения более или менее одинакового результата.
Количественное ограничение фактически ограничено способами его использования. Недостаточно знать, что ограничение действует для всех типов, вам также нужен способ узнать, на каких типах вам нужно его специализировать. GHC достигает этого, представляя количественные ограничения в виде своего рода локального экземпляра, на который распространяется общие правила разрешения экземпляров. Я не уверен, что количественные равенства типов вообще что-то значат. Таким образом, недостаточно, чтобы квантифицированное ограничение можно быть создано для того, чтобы делать то, что мы хотим; эта инстанцирация должна происходить в обычном порядке разрешения экземпляра, и это накладывает ограничения на разрешенные формы количественных ограничений.
Хитрость заключается в том, чтобы определить синоним класса для тела количественного ограничения:
class (Bar f ~ Bar (f a)) => EBar f a
instance (Bar f ~ Bar (f a)) => EBar f a
Таким образом, мы можем переписать количественное ограничение как forall x. EBar f x
, семейств типов не видно:
class (Foo f, forall a. Foo (f a), forall x. EBar f x) => Foo' f where
Чтобы использовать этот класс, нам нужна явная функция для специализации количественного ограничения (проблема в том, что если мы используем равенство Bar f ~ Bar (f a)
напрямую, средство проверки типов не сможет связать его с количественным ограничением forall x. EBar f x
, которое на него не похоже):
-- Morally a function on constraints `(forall x. EBar f x) => EBar f a`
-- in continuation-passing style: (x => y) is isomorphic to forall r. (y => r) -> (x => r)
ebar :: forall f a r. (EBar f a => Proxy (f a) -> r) -> (forall x. EBar f x) => Proxy (f a) -> r
ebar f = f
test :: Foo' f => Proxy (f a) -> Bar (f a) -> Bar f
test = ebar (\_ -> id)
{-# LANGUAGE RankNTypes, MultiParamTypeClasses, FlexibleInstances, TypeFamilies, PolyKinds, QuantifiedConstraints #-}
import Data.Proxy
class Foo f where
type Bar f
class (Foo f, forall a. Foo (f a), forall a. EBar f a) => Foo' f where
class (Bar f ~ Bar (f a)) => EBar f a
instance (Bar f ~ Bar (f a)) => EBar f a
ebar :: forall f a r. (EBar f a => Proxy (f a) -> r) -> (forall x. EBar f x) => Proxy (f a) -> r
ebar f = f
test :: Foo' f => Proxy (f a) -> Bar (f a) -> Bar f
test = ebar (\_ -> id)
person
Li-yao Xia
schedule
15.07.2020
test
GHC хочетBar (f a) ~ Bar f
, гдеa
— аргумент дляtest
. Он имеетforall b. Bar (f b) ~ Bar f
. Ему нужно создать экземпляр этого равенства, чтобы получить конкретное, поэтому я думаю, что он пытается найти экземпляр, создавая переменную объединения_b
, создавая экземплярb := _b
, а затем объединяя полученныеBar (f _b)
сBar (f a)
для решения для_b
. Однако он не может уменьшитьBar (f _b) ~ Bar (f a)
доf _b ~ f a
, а затем_b ~ a
, потому что первый шаг требует, чтобыBar
было инъективным. Не знаю, как решить это автоматически. - person HTNW   schedule 15.07.2020Foo'
, не говоря уже о самомtest
(он говорит мне о недопустимом приложении семейства синонимов типа «Bar f», например: Bar f ~ Bar (f a)). Какую версию GHC вы используете? Я пробовал 8.8 и 8.10, и они оба дают ту же ошибку. - person Daniel Wagner   schedule 15.07.2020newtype Wrap f = Wrap (Bar f)
и используя ее в определении класса. Если я также использую его вtest
, ошибка изменится наcannot construct the infinite type: f ~ f a
. - person Greg C   schedule 15.07.2020