Typeclass projections as inheritance

60 views Asked by At

I want to allow typeclasses to be easily "inherited" on union types, by deriving the typeclass automatically when there exists a projection (this projection is another typeclass that I defined separately). Below is some code illustrating what I'm trying to achieve:

class t ##-> a where -- the projection
    inj :: t -> a
    prj :: a -> Maybe t
class Prop t where -- the property
    fn1 :: Int -> t
    fn2 :: t -> t
instance (t #-> a, Prop t) => Prop a where -- deriving the property from the projection
    cst :: Int -> a
    cst = inj . fn1 @t
    fn2 :: a -> a
    fn2 a1 = inj $ fn2 @t $ fromJust (prj a1)

And so, when I define a sum type, I can define only the projection #->, without redefining the typeclass Prop on the sum type.

data K = ...
instance Prop K
data SumType = E1 K | ...
instance K #-> SumType where
    inj :: K -> SumType 
    inj = E1
    prj :: SumType -> Maybe K
    prj (E1 k) = Just k
    prj _ = Nothing 

However, I ran into the following problem, when I would like to reuse typeclass functions in the instance definition. For example, when I am trying to give a Prop class definition for a base type (say, String):

instance Prop String where
    cst :: Int -> String
    cst = show
    fn2 :: String -> String
    fn2 s = if s == cst 0 then "0" else s -- Overlapping instances for Prop String arising from a use of ‘cst’

The compiler isn't sure whether to use the derived type (from the #-> derivation), or to use the base instance I defined (i.e. the Prop String definition). It seems obvious to me however, that in the Prop String definition, the cst @String definition should be used. Using {-# LANGUAGE TypeApplications #-} also does not seem to help the compiler determine the instance needed.

I'm wondering how would we go about convincing the compiler to use the instance I intended here?

0

There are 0 answers