haskellcategory-abstractions

Difficulty in defining the Relation type as an instance of the Category class


I'm trying to define Relation a b as a Category instance. It seems to me that the composer operator is well defined and respects the associative law. When it comes to the id, I can't find a correct definition. What am I doing wrong?

import Data.Map as M
import Data.Set as S
import Control.Category as Cat

newtype Relation a b = R (Map a (Set b)) deriving (Show, Eq)

-- instance Cat.Category Relation where
    -- id = 
    -- (.) = (°)

-- GHC.Base.id r1
-- > R (fromList [(10,fromList "abef"),(30,fromList "GRTXa")])

r1 = R $ M.fromList [(10,S.fromList "abfe"),(30,S.fromList "aXGRT")]
r2 = R $ M.fromList [('a',S.fromList [Just "Apple",Just "Ask"]),('b',S.fromList [Just "book",Just "brother"]),('T',S.fromList [Just "Table"]),('?',S.fromList [Just "Apple",Just "brother"])]

-- ex. r1 ° r2 = R (fromList [(10,fromList [Just "Apple",Just "Ask",Just "book",Just "brother"]),(30,fromList [Just "Apple",Just "Ask",Just "Table"])])


(°) :: (Ord a, Ord k, Ord b) => Relation a k -> Relation k b -> Relation a b   
R mp1 ° R mp2
  | M.null mp1 || M.null mp2 = R M.empty 
  | otherwise = R $ M.foldrWithKey (\k s acc -> M.insert k (S.foldr (\x acc2 -> case M.lookup x mp2 of
                                                                                  Nothing -> acc2
                                                                                  Just s2 -> S.union s2 acc2
                                                                    ) S.empty s) acc
                                   ) M.empty mp1

Solution

  • Relation cannot be an instance of Category:

    1. as luqui points out in the comments, Relation only represents finite relations (when viewed as sets of pairs), but the identity relation on an infinite set is infinite;

    2. composition is not defined on all types, only on instances of Ord.

    Here's one way to address those issues and make Relation an instance of Category:

    1. add a trivial element to represent the identity relation (this is the same idea behind making a monoid out of a semigroup with Option);
    2. make the other "nontrivial" relations carry the Ord instances.

    This can be done using GADTs.

    {-# LANGUAGE GADTs #-}
    
    data Relation a b where
      Id :: Relation a a
      R :: (Ord a, Ord b) => Map a (Set b) -> Relation a b
    
    instance Category Relation where
      id = Id
      Id . r = r
      r . Id = r
      R r1 . R r2 = ...