尝试将类型Relation [[a,b]]定义为Category类的实例时出错

时间:2019-05-24 17:25:16

标签: haskell

我正在探索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')]

1 个答案:

答案 0 :(得分:2)

如果您将Eq约束存储在GADT中,则无需从签名中进行要求:通过对关系值进行模式匹配,约束将已经在范围内。因此,只需将签名更改为

(°) :: RelationT t b -> RelationT a t -> RelationT a b

(Eq a, Eq b, Eq c)信息将在函数体内仍然可用,因为您已经在其中将RT r1RT 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
  (.) = (°)