我正在探索AxB的R子集的一些可能实现,每种实现都有其局限性和可能性。我还希望尽可能将它们定义为Category类或Semigroupoid类的实例。
我选择了成对的列表,因为对于组成列表的操作,它仅将成对元素的类型放置为Eq类实例的约束。
现在,我陷入了这个编译器错误消息:“没有任何因使用'°'而引起的(Eq a)实例”
怎么了?
{-# LANGUAGE GADTs #-}
module RelationT where
import Data.List
import Control.Category as Cat
data RelationT a b where
Id :: RelationT a a
RT :: (Eq a, Eq b) => [(a,b)] -> RelationT a b
instance Category RelationT where
id = Id
Id . r = r
r . Id = r
r1 . r2 = r1 ° r2 -- error: No instance for (Eq a) arising from a use of ‘°’
(°) :: (Eq a, Eq b, Eq t) => RelationT t b -> RelationT a t -> RelationT a b
RT r1 ° RT r2 = RT $ nub $ go r1 r2
where
go [] r = []
go r [] = []
go xys2 ( ((x1,y1): xys1)) = go2 x1 y1 xys2 [] ++ go xys2 xys1
where
go2 x y [] acc = acc
go2 x y ((w,z):wzs) acc
| y == w = go2 x y wzs ((x,z):acc)
| otherwise = go2 x y wzs acc
-- ex. RT [(1,'a'),(4,'b'),(5,'c'),(10,'d')] ° RT [(3,10),(1,5),(1,1)]
-- > RT [(3,'d'),(1,'c'),(1,'a')]
答案 0 :(得分:2)
如果您将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 具有完全相同的对象的类别,即所有Haskell类型。但是您的关系类别实际上仅具有相等的可比较类型作为对象。使用额外的Id
构造函数,您将其强制扩展为还包括非eq类型之间的关系,但是仅 身份关系可用...这是一个脆弱的技巧。 / p>
正确的解决方法是使用类型类,该类型类允许类别从对象开始就具有更严格的概念。最简单的方法就是约束种类。来自我的constrained-categories
package:
{-# 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
(.) = (°)