
class Cls c where type Ret c :: (Bar *) => * -- or a better name foo :: c -> Ret c
which isn't legal Haskell.
OK, that's exactly the same thing I've met when developing compose-trans. I needed guarantees that something is a Monad. My way of doing that was to make "Bar" ("Monad" in my case) a datatype. Suppose you "Bar" class is something like class Bar c where toBar :: String -> c changeBar :: c -> Int -> c fromBar :: c -> c -> [Float] Declare something like data BarD c = BarD {toBarD :: String -> c, changeBarD :: c -> Int -> c, fromBarD :: c -> c -> [Float]} I've did it some other way, using the Monad specifics, but essentially it was the same. Then you can write a default "BarD" this way: barDInst :: Bar c => BarD c barDInst = BarD {toBarD = toBar, changeBarD = changeBar, fromBarD = fromBar} Do not (!) export BarD constructor, so the only BarD one would be able to produce would be the default one. It simplifies you interface. Now, your "Cls" looks like that: class Cls c where type Ret c barRet :: BarD (Ret c) foo :: c -> Ret c If somebody is using your class, she can't be sure that "Ret c" is of class "Bar", but she would have sort of an instance anyway: she would just use "toBarD barRet" instead of "toBar", and so on. If somebody is trying to make some "c" an instance of "Cls" - the only thing she can do is to make some "d" an instance of "Bar" and write instance Cls MyCoolInstance where type Ret MyCoolInstance = MyCoolRetType barRet = barDInst foo c = ... It's higly possible, however, that you'd have to deal with "Ambiguous type variable"'s.