У меня есть функция foo
со множеством ограничений. Конечно, эти ограничения должны появляться в сигнатурах функций, использующих foo
, поэтому я пытаюсь обернуть ограничения foo
синонимом типа FooCtx a b ... :: Constraint
. Например,
foo :: (A a, B b, C c, ...) => a -> b -> c
bar :: (A a, B b, C c, ...) ...
bar = ... foo ...
станет
type FooCtx a b c ... = (A a, B b, C c, ...)
foo :: (FooCtx a b c) => a -> b -> c
bar :: (FooCtx a b c) => ...
Это прекрасно работает, если все типы выставлены. Однако я использую функциональные зависимости для создания некоторых типов в списке ограничений, и эти типы не отображаются в сигнатуре foo
. Например:
class Bar a b | a -> b
foo (Bar a b, ...) => a -> a
GHC не примет type FooCtx a = (Bar a b)
, потому что b
не привязан к LHS. Я также не могу использовать type FooCtx a b = (Bar a b)
, потому что b
не входит в область действия подписи foo
. Подпись foo
будет foo :: (FooCtx a ?) => a -> a
.
Одно из неудовлетворительных решений — поместить одно из ограничений в подпись foo
с FooCtx
, чтобы ввести тип fundep в область видимости:
class Bar a b | a -> b
type FooCtx a b = ...
foo (Bar a b, FooCtx a b) => a -> a
но это противоречит цели группировки ограничений:
Пока я не столкнулся с этим случаем, я предполагал, что синонимы ограничений можно слепо заменять произвольными списками ограничений. Единственный известный мне способ инкапсулировать такие ограничения — с помощью класса, но он страдает от той же проблемы: class (A a, B b, C c, ...) => FooCtx a b c
не может иметь никаких скрытых типов в LHS. Есть ли какой-то другой способ, которым я мог бы полностью собрать все эти ограничения?
type FooCtx a b = ...
- это всего на 2 символа длиннее, чемtype FooCtx a = ...
. - person user2407038   schedule 03.03.2015foo
, потому чтоb
не входит в область действия подписиfoo
. - person crockeea   schedule 03.03.2015TypeFamilies
вместоFunctionalDependencies
? - person Cirdec   schedule 03.03.2015