The problem is that, when you're unwrapping Sub Dict
, there needs to be some v
in scope to which the dictionary is going to apply, but there is none. So the compiler tries to create a fresh var v0
in hopes to unify it with something eventually, but the eventuality never comes.
But let's think: where would that v
ultimately come from? Well, what does unwrapping Sub Dict
give us? It gives us a less v
(for some as of yet unknown v
), provided we can give it more v
. And what is the place where we just happen to have more v
and need less v
? Why it's inside of Foo
's second argument of course!
So this means that we need to unwrap Sub Dict
only when the second argument of Foo
is actually called. At that point, whoever called it will have provided an instance of more v
, so we'll be able to give it to Sub Dict
and get out a less v
.
So naively it would look something like this:
restrictFoo e (Foo proxy f) =
Foo (Proxy @more) x -> case e of Sub Dict -> f x
But this still doesn't work: the compiler doesn't know that the type of x
is the same type that it needs to use for Sub Dict
. There is no connection between the two!
To create such connection, we need to explicitly quantify over the type of x
, and to do that, we need to give the new function a type signature:
restrictFoo e (Foo proxy f) =
Foo (Proxy @more) f'
where
f' :: forall x. more x => x -> x
f' x = case e of Sub (Dict :: Dict (less x)) -> f x
Now this works: whenever somebody takes f'
out of the Foo
and calls it, they will have to provide an instance of more x
, and having that instance in scope will allow us to give it to Sub
and get out a Dict (less x)
, which gives us an instance of less x
, which finally allows us to call the original f
.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…