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?