Учитывая некоторые определения типов:
data A
data B (f :: * -> *)
data X (k :: *)
… И этот класс типов:
class C k a | k -> a
… Эти (очень надуманные для целей минимального примера) определения функций typecheck:
f :: forall f. (forall k. (C k (B f)) => f k) -> A
f _ = undefined
g :: (forall k. (C k (B X)) => X k) -> A
g = f
Однако, если мы используем семейство типов вместо класса с функциональной зависимостью:
type family F (k :: *)
… Тогда эквивалентные определения функций не могут быть проверены по типу:
f :: forall f. (forall k. (F k ~ B f) => f k) -> A
f _ = undefined
g :: (forall k. (F k ~ B X) => X k) -> A
g = f
• Couldn't match type ‘f0’ with ‘X’
‘f0’ is untouchable
inside the constraints: F k ~ B f0
bound by a type expected by the context:
F k ~ B f0 => f0 k
Expected type: f0 k
Actual type: X k
• In the expression: f
In an equation for ‘g’: g = f
I read Section 5.2 of the OutsideIn(X) paper, which describes touchable and untouchable type variables, and I sort of understand what’s going on here. If I add an extra argument to f
that pushes the choice of f
outside the inner forall
, then the program typechecks:
f :: forall f a. f a -> (forall k. (F k ~ B f) => f k) -> A
f _ _ = undefined
g :: forall a. X a -> (forall k. (F k ~ B X) => X k) -> A
g = f
Однако в этом конкретном примере меня так смутило то, почему функциональная зависимость имеет другое поведение. Я слышал, как люди в разное время утверждали, что функциональные зависимости, подобные этой, эквивалентны семейству типов плюс равенство, но это демонстрирует, что на самом деле это не так.
Какую информацию предоставляет в этом случае функциональная зависимость, позволяющая создать экземпляр f
способом, который не позволяет семейство типов?
g = f @ X
также набирает проверки. Кажется, что алгоритм вывода не обязуется выбирать переменную типаf
какX
. Я не понимаю почему - обычно это потому, что может быть другое значениеf
, делающее тип(forall k. (F k ~ B f) => f k) -> A
равным(forall k. (F k ~ B X) => X k) -> A
. Здесьf ~ X
кажется мне единственным решением (не так ли?). Интересный. - person chi   schedule 10.12.2017f
извлекается изB f
), но из ограничения равенства это это невозможно. - person leftaroundabout   schedule 10.12.2017type instance F Int = Bool
превращается вf_int :: F Int ~ Bool
. Fundeps просто проявляется как ограничения во время объединения, они не влияют на принуждение. Вот почему трудно конвертировать между ними. - person Benjamin Hodgson♦   schedule 10.12.2017f
. Потому что вы не объявили никаких экземпляров дляC
(и получилиf
для работы с ними). Проверка семейства типов более активна, чем для FunDeps. Таким образом, у вас может быть, что на самом деле две формы эквивалентны в том смысле: форма семейства Type не компилируется; форма FunDep не имеет жилых экземпляров дляf
. Возможно поэтому вы можете определить толькоf _ = undefined
. Итак, объявите экземпляр дляC
; попробуйте применитьf
к чему-нибудь. Аналогично попробуйте применитьg
. - person AntC   schedule 11.12.2017C
фактическиReifies
изData.Reflection
. Уверяю вас, он действительно выполняет проверку типов без каких-либо оснований где-либо и после примененияg
. Вы даже можете настроить этот пример для проверки типов без оснований, изменивC
наReifies
и изменив определениеf
наf m = reify B (\(_ :: Proxy s) -> m @s
seq` A) `(после того, как сделалиA
иB
обитаемыми). - person Alexis King   schedule 11.12.2017g = f
, да, это исправляет типы s.t.f ~ X
. Если вы удалите определение дляg
, тогдаf
может быть любым типом конструктора, напримерMaybe
. - person AntC   schedule 02.01.2018