3

I'm trying to implement the Cantor Pairing Function, as an instance of a generic Pair typeclass, as so:

module Pair (Pair, CantorPair) where

-- Pair interface
class Pair p where
    pi :: a -> a -> p a
    k :: p a -> a
    l :: p a -> a

-- Wrapper for typing
newtype CantorPair a = P { unP :: a }

-- Assume two functions with signatures:
cantorPair :: Integral a => a -> a -> CantorPair a
cantorUnpair :: Integral a => CantorPair a -> (a, a)

-- I need to somehow add an Integral a constraint to this instance,
-- but I can't work out how to do it.
instance Pair CantorPair where
    pi = cantorPair
    k = fst . cantorUnpair
    l = snd . cantorUnpair

How can I add the appropriate Integral constraint to the instance? I have a vague feeling I might need to modify the Pair interface itself, but not sure how to go about this.

3 Answers 3

2

If you have access to the class definition, you can add an Integral constraint to the pi, k, and l methods. This is a bit unsatisfactory, though: there's nothing saying that Integral is going to be the right constraint for all instances, and you after all don't want to reject some instances just because you didn't have enough foresight. So, here's one generalization: we'll allow the constraint to vary in each instance.

{-# LANGUAGE ConstraintKinds, TypeFamilies #-}
import GHC.Exts

newtype CantorPair a = P { unP :: a }
cantorPair :: Integral a => a -> a -> CantorPair a
cantorUnpair :: Integral a => CantorPair a -> (a, a)
cantorPair = undefined
cantorUnpair = undefined

class Pair p where
    type Ctxt p a :: Constraint
    pi :: Ctxt p a => a -> a -> p a
    k  :: Ctxt p a => p a -> a
    l  :: Ctxt p a => p a -> a

instance Pair CantorPair where
    type Ctxt CantorPair a = Integral a
    pi = cantorPair
    k  = fst . cantorUnpair
    l  = snd . cantorUnpair

-- just for fun
data DataPair a = DataPair a a

instance Pair DataPair where
    type Ctxt DataPair a = ()
    pi = DataPair
    k (DataPair a _) = a
    l (DataPair _ a) = a

-- this one made GHC panic! neat, I get to file a bug
data Unit a = Unit

instance Pair Unit where
    type Ctxt Unit a = a ~ ()
    pi _ _ = Unit
    k _ = ()
    l _ = ()
Sign up to request clarification or add additional context in comments.

Comments

1

Do you want all pairs to always contain integral elements? In this case, you could add the constraint to the signatures of the methods:

class Pair p where
  pi :: Integral i => i -> i -> p i
  k :: Integral i => p i -> i
  l :: Integral i => p i -> i

This will make you pair class less general but will ensure that your CantorPair type could be a part of it.

If you want to keep your Pair class somewhat general, you could use a multi parameter type class. (This will require two extensions: MultiParamTypeClasses and FlexibleInstances.)

class Pair p a where
  pi :: a -> a -> p a
  k :: p a -> a
  l :: p a -> a

instance Integral i => Pair CantorPair i where
    pi = cantorPair
    k = fst . cantorUnpair
    l = snd . cantorUnpair

I don't know if this is necessarily the best option from a design standpoint, but it's a good way to learn about how multiparamter type classes work. (Which, admittedly, is fairly simple.)

2 Comments

Well, at this stage I think I will always be using Integrals, but I didn't think it made sense to add that constraint to Pair. For example, another instance could be added such as data IdentityPair a = I a a instance Pair IdentityPair where pi x y = I x y k (I x _) = x l (I _ y) = y
Aah, thanks! I thought I'd need MultiParamTypeClasses, but couldn't get my fiddling to work. Must've been because I wasn't using FlexibleInstances.
1

This solution uses type families, so you need -XTypeFamilies. We put the type constraint on the type itself, instead of the type constructor:

class Pair p where
    type First p :: *
    type Second p :: *
    pi :: First p -> Second p -> p
    k :: p -> First p
    l :: p -> Second p

and then we create the instances like:

instance Integral a => Pair (CantorPair a) where
    type First (CantorPair a) = a
    type Second (CantorPair a) = a
    pi = cantorPair
    k = fst . cantorUnpair
    l = snd . cantorUnpair

instance Pair (a, b) where
    type First (a, b) = a
    type Second (a, b) = b
    pi = (,)
    k = fst
    l = snd

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.