Если вы сохраняете ограничения Eq
в GADT, нет необходимости требовать этого от сигнатуры: при сопоставлении с образцом для значений отношения ограничение уже будет в области видимости.Итак, просто измените сигнатуру на
(°) :: RelationT t b -> RelationT a t -> RelationT a b
Информация (Eq a, Eq b, Eq c)
будет все еще доступна в теле функции, поскольку вы сопоставили с шаблоном RT r1
и RT r2
там, который, если успешно, свидетельствует о том, что все типы имеют экземпляр Eq
.
Это говорит: по моему опыту, этот трюк хранения ограничения в GADT быстро приводит к неприятностям, когда вы хотите сделать больше вовлеченнымвещи с вашей категорией.Проблема в том, что стандартный класс Category
на самом деле не подходит для такого типа отношения, потому что он поддерживает только те категории, которые имеют точно такие же объекты, как Hask - т.е. всеХаскель типы.Но ваша категория отношений действительно имеет только сопоставимые по равенству типы в качестве объектов;с помощью дополнительного конструктора Id
вы принудительно расширяете его, чтобы также включить отношения между неэквивалентными типами, но есть только отношение идентичности доступно ... это довольно хрупкий взлом.
Правильным выходом является использование класса типов, который позволяет категориям иметь более ограниченное представление об объектах для начала.Самый простой способ сделать это - виды ограничений .Из моего constrained-categories
пакета :
{-# LANGUAGE TypeFamilies, ConstraintKinds #-}
import GHC.Exts (Constraint)
class Category k where
type Object k o :: Constraint
id :: Object k a => k a a
(.) :: (Object k a, Object k b, Object k c)
=> k b c -> k a b -> k a c
Тогда вы можете сделать экземпляр
data RelationT a b where
Id :: RelationT a a
RT :: [(a,b)] -> RelationT a b
instance Category RelationT where
type Object RelationT o = Eq o
id = Id
(.) = (°)