Let's start by looking at how Dict and (:-) should be used.
ordToEq :: Dict (Ord a) -> Dict (Eq a) ordToEq Dict = Dict
Matching the pattern with a Dict value of type Dict (Ord a) limits Ord a to the scope from which Eq a can be inferred (since Eq is the superclass of Ord ), so Dict :: Dict (Eq a) well-printed.
ordEntailsEq :: Ord a :- Eq a ordEntailsEq = Sub Dict
Similarly, Sub introduces an input constraint to the scope for the duration of its argument, allowing it to correctly enter this Dict :: Dict (Eq a) .
However, while pattern matching on a Dict results in a scope constraint, pattern matching in a Sub Dict does not enforce any new constraint conversion rule. In fact, if the input constraint is no longer in scope, you cannot match patterns on Sub Dict at all.
So, this explains your error of the first type, "Could not deduce (ac) arising from a pattern" : you tried to map the image to a Sub Dict , but the input restriction ac was not yet in scope.
Another type of error, of course, suggests that the constraints you were able to get in scope were not enough to satisfy the RecAll f rs b constraint. So which parts are needed and which are missing? Let's look at the definition of RecAll .
type family RecAll f rs c :: Constraint where RecAll f [] c = () RecAll f (r : rs) c = (c (fr), RecAll f rs c)
Yeah! RecAll is a type family that is as invaluable as it is, with fully abstract rs , the RecAll f rs c constraint is a black box that cannot be made from any set of smaller parts. Once we specialize rs to [] or (r : rs) , it becomes clear which fragments we need:
recAllNil :: Dict (RecAll f '[] c) recAllNil = Dict recAllCons :: p rs -> Dict (c (fr)) -> Dict (RecAll f rs c) -> Dict (RecAll f (r ': rs) c) recAllCons _ Dict Dict = Dict
I use p rs instead of Proxy rs because it is more flexible: if I had Rec f rs , for example, I could use it as my proxy with p ~ Rec f .
Then, to implement the version above with (:-) instead of Dict :
weakenNil :: RecAll f '[] c1 :- RecAll f '[] c2 weakenNil = Sub Dict weakenCons :: p rs -> c1 (fr) :- c2 (fr) -> RecAll f rs c1 :- RecAll f rs c2 -> RecAll f (r ': rs) c1 :- RecAll f (r ': rs) c2 weakenCons _ entailsF entailsR = Sub $ case (entailsF, entailsR) of (Sub Dict, Sub Dict) -> Dict
Sub introduces the RecAll f (r ': rs) c1 input restriction into the scope for the duration of its argument, which we organized to include the rest of the function body. The equation for the RecAll f (r ': rs) c1 type family expands to (c1 (fr), RecAll f rs c1) , which thus also enters the scope. The fact that they are in scope allows us to map patterns on two Sub Dict , and those two Dict bring their limitations in scope: c2 (fr) and RecAll f rs c2 . These two are what the RecAll f (r ': rs) c2 target constraint RecAll f (r ': rs) c2 , so our right side of the Dict well-typed.
To complete our implementation of weakenAllRec , we need to map the template to rs to determine if weakenNil or weakenCons should be delegated. But since rs is at the type level, we cannot directly match it to a template. The Hasochism document explains how to map patterns at the Nat level, we need to create a Natty shell data type. The way Natty works is that each of its constructors is indexed by the corresponding Nat constructor, so when we map a pattern to the Natty constructor at the value level, the corresponding constructor is also implied at the type level. We could define such a wrapper for lists of type types such as rs , but it happens that Rec f rs already has constructors corresponding to [] and (:) , and the calling elements weakenAllRec will probably have one of which lies around.
weakenRecAll :: Rec f rs -> (forall a. c1 a :- c2 a) -> RecAll f rs c1 :- RecAll f rs c2 weakenRecAll RNil entails = weakenNil weakenRecAll (fx :& rs) entails = weakenCons rs entails $ weakenRecAll rs entails
Note that the entails type must be forall a. c1 a :- c2 a forall a. c1 a :- c2 a , not just c1 a :- c2 a , because we donβt want to say that weakenRecAll will work for any a choice of the caller, we want the caller to call that c1 a entails c2 a for each a .