Proving two dependent (AVL tree key-value) pairs equal - agda

The standard library's AVL tree implementation uses dependent pairs to store key-value pairs. I have two such pairs whose keys (k and k′) I have shown to be equal (k≡k′). They also contain the same value (v). I'd like to prove that the pairs are equal. Here's the goal:
open import Agda.Primitive
open import Data.Nat
open import Data.Nat.Properties
open import Data.Product
open import Relation.Binary.PropositionalEquality
open import Data.AVL.Indexed <-strictTotalOrder
module Repro (ℓ : Level) (V : Value ℓ) where
Val = Value.family V
V≈ = Value.respects V
proof : (k k′ : ℕ) → (v : Val k) → (k≡k′ : k ≡ k′) → (k , v) ≡ (k′ , V≈ k≡k′ v)
proof k k′ v k≡k′ = {!!}
I tried rewriting with k≡k′, which turns k on the LHS into k′ and k≡k′ on the RHS into refl. That's where I get stuck. I feel like I'm missing something obvious, as this seems a pretty basic thing to do.
(As an exercise, I'm trying to prove the standard library's AVL tree insertion correct. Hence my recent obsession with its AVL tree code.)
Update
Hmm. Maybe this isn't as trivial as I thought, at least without knowing more about V. After all, what I currently know is that:
There's a way to turn a value of type Val k into a value of type Val k′ - by way of V≈.
That V≈ refl takes a Val foo and returns a Val foo.
What I don't know at this point is that V≈ refl is the identity function, which seems to be what I'd need to do my proof.
Update II
If, for example, I knew that V≈ was actually subst Val, then my proof would be:
proof′ : (k k′ : ℕ) → (v : Val k) → (k≡k′ : k ≡ k′) → (k , v) ≡ (k′ , subst Val k≡k′ v)
proof′ k k′ v k≡k′ rewrite k≡k′ = cong (k′ ,_) refl
So, I guess my question ultimately is:
Can I complete my original proof with what I have?
If not, then what property do I need V≈ to have to complete my original proof (i.e., to "restrict it to functions that are like subst")?
Update III
I am providing more context in response to MrO's comment. Instead of stripping down my actual proof, I'll provide something even simpler. Let's prove a special case of AVL tree insertion, which leads to the same problem that I'm facing in the general case.
Let's prove that inserting a value v for key k into an empty tree and then looking up the value for key k yields the value v that we inserted.
Let's get set up:
open import Agda.Primitive using (Level)
open import Data.Maybe using (just)
open import Data.Nat using (ℕ)
open import Data.Nat.Properties using (<-strictTotalOrder ; <-cmp)
open import Data.Product using (proj₂)
open import Function using () renaming (const to constᶠ)
open import Relation.Binary using (tri< ; tri≈ ; tri>)
open import Relation.Binary.PropositionalEquality using (_≡_ ; refl )
open import Relation.Nullary.Negation using (contradiction)
open import Data.AVL.Indexed <-strictTotalOrder
module Simple {ℓ : Level} (V : Value ℓ) where
private
Val = Value.family V
V≈ = Value.respects V
Now, a function to create an empty tree:
make-empty : ∀ {l u} → l <⁺ u → Tree V l u 0
make-empty = leaf
And the proof:
proof : ∀ {l u} (k : ℕ) (v : Val k) (l<u : l <⁺ u) (l<k<u : l < k < u) →
lookup k (proj₂ (insertWith k (constᶠ v) (make-empty l<u) l<k<u)) l<k<u ≡ just v
proof k v l<u l<k<u with <-cmp k k
... | tri< _ p _ = contradiction refl p
... | tri> _ p _ = contradiction refl p
... | tri≈ _ p _ rewrite p = {!!}
The goal that I'm trying to fill in is just (V≈ refl v) ≡ just v.

You could postulate, or take as a module parameter, the fact that V≈ leaves the value unchanged, as follows:
postulate lemma : ∀ {k k′} {v : Val k} → (k≡k′ : k ≡ k′) → v ≡ subst _ (sym k≡k′) (V≈ k≡k′ v)
After that, your proof becomes:
proof : (k k′ : ℕ) → (v : Val k) → (k≡k′ : k ≡ k′) → (k , v) ≡ (k′ , V≈ k≡k′ v)
proof k ._ _ refl = cong (k ,_) (lemma refl)

Related

How to tell Agda to unfold a definition to prove an equivalency

I have a function like this:
open import Data.Char
open import Data.Nat
open import Data.Bool
open import Relation.Binary.PropositionalEquality
open Relation.Binary.PropositionalEquality.≡-Reasoning
foo : Char → Char → ℕ
foo c1 c2 with c1 == c2
... | true = 123
... | false = 456
I would like to prove that when I call it with the same argument (foo c c), it always yields 123:
foo-eq⇒123 : ∀ (c : Char) → foo c c ≡ 123
foo-eq⇒123 c =
foo c c ≡⟨ {!!} ⟩
123 ∎
I am able to use refl to prove a trivial example:
foo-example : foo 'a' 'a' ≡ 123
foo-example = refl
So, I would think that I could just put refl in the hole since all Agda needs to do is beta-reduce foo c c. However, replacing the hole with refl yields the following error:
.../Unfold.agda:15,14-18
(foo c c
| Relation.Nullary.Decidable.Core.isYes
(Relation.Nullary.Decidable.Core.map′ Data.Char.Properties.≈⇒≡
Data.Char.Properties.≈-reflexive
(Relation.Nullary.Decidable.Core.map′
(Data.Nat.Properties.≡ᵇ⇒≡ (toℕ c) (toℕ c))
(Data.Nat.Properties.≡⇒≡ᵇ (toℕ c) (toℕ c)) (T? (toℕ c ≡ᵇ toℕ c)))))
!= 123 of type ℕ
when checking that the expression refl has type foo c c ≡ 123
I suspect the issue is that Agda doesn't automatically understand that c == c is true for all c:
c==c : ∀ (c : Char) → (c == c) ≡ true
c==c c = refl
.../Unfold.agda:23,10-14
Relation.Nullary.Decidable.Core.isYes
(Relation.Nullary.Decidable.Core.map′ Data.Char.Properties.≈⇒≡
Data.Char.Properties.≈-reflexive
(Relation.Nullary.Decidable.Core.map′
(Data.Nat.Properties.≡ᵇ⇒≡ (toℕ c) (toℕ c))
(Data.Nat.Properties.≡⇒≡ᵇ (toℕ c) (toℕ c)) (T? (toℕ c ≡ᵇ toℕ c))))
!= true of type Bool
when checking that the expression refl has type (c == c) ≡ true
So, what is the right way to prove my foo-eq⇒123 theorem?
Agda's built-in Char type is a little weird. Let's contrast it with a non-weird built-in type, ℕ. The equality test for ℕ looks as follows.
_≡ᵇ_ : Nat → Nat → Bool
zero ≡ᵇ zero = true
suc n ≡ᵇ suc m = n ≡ᵇ m
_ ≡ᵇ _ = false
Note that n ≡ᵇ n doesn't reduce to true, either. After all, Agda doesn't know whether n is zero or suc, i.e., which of the two clauses to apply for the reduction. So, this has the same problem as your Char example. But for ℕ there is an easy way out.
Let's look at your example again and let's also add a ℕ-based version of foo, bar.
open import Data.Char using (Char ; _==_ ; _≟_)
open import Data.Nat using (ℕ ; zero ; suc ; _≡ᵇ_)
open import Data.Bool using (true ; false)
open import Relation.Binary.PropositionalEquality using (_≡_ ; refl)
open import Relation.Nullary using (yes ; no)
open import Relation.Nullary.Negation using (contradiction)
foo : Char → Char → ℕ
foo c1 c2 with c1 == c2
... | true = 123
... | false = 456
bar : ℕ → ℕ → ℕ
bar n1 n2 with n1 ≡ᵇ n2
... | true = 123
... | false = 456
So, what's the easy way out for ℕ? We pattern match / do a case split on n to reduce n ≡ᵇ n just enough to do our proof. I.e., either to the base case zero or to the next recursive step suc n.
bar-eq⇒123 : ∀ n → bar n n ≡ 123
bar-eq⇒123 zero = refl
bar-eq⇒123 (suc n) = bar-eq⇒123 n
ℕ just has two constructors and we know what ≡ᵇ looks like, so pattern matching is straight forward.
For Char, things are way more complicated. Long story short, the equality test for Char is defined in terms of a function toℕ that Agda doesn't give us a definition for. Also, the Char data type is postulated and doesn't come with any constructors. So a proof like bar-eq⇒123 is not an option for Char. We don't have clauses and we don't have constructors. (I wouldn't call, say, 'a' a constructor for Char. Similarly to how 1234 is not a constructor for ℕ.)
So, what could we do? Note that c == c in the error message that you quoted reduces to something pretty complicated that involves isYes. If we reduced c == c only a tiny little bit (instead of as much as possible), we'd get isYes (c ≟ c) (instead of the complicated thing from the error message).
_≟_ is the decidable equality for Char (i.e., a combination of an "Equal or not?" Boolean and a proof). isYes strips away the proof and gives us the Boolean.
My idea for a proof would then be to not do a case split on c (as we did for ℕ), but on c ≟ c. This would yield two cases and reduce the isYes to true or false, respectively. The true case would be obvious. The false case could be dicharged by contradiction with the proof from the decidable equality.
foo-eq⇒123 : ∀ c → foo c c ≡ 123
foo-eq⇒123 c with c ≟ c
... | yes p = refl
... | no ¬p = contradiction refl ¬p
Note that, in turn, this proof doesn't easily translate to ℕ, as _≡ᵇ_ isn't based on decidable equality and isYes. It's a primitive instead.
Maybe an even better idea would be to consistently stick to decidable equality, for Char as well as ℕ instead of using _==_ or _≡ᵇ_. foo would then look as follows.
baz : Char → Char → ℕ
baz c1 c2 with c1 ≟ c2
... | yes p = 123
... | no ¬p = 456
And the foo-eq⇒123 proof would apply to it unchanged.

Why is Agda refusing Set₁ → Set₁?

Here's the code:
Continuation : Set → Set₁ → Set₁
Continuation R X = (X → R) → R
Selection : Set → Set₁ → Set₁
Selection R X = (X → R) → X
dagger : {R : Set} → Selection R → Continuation R
dagger S X k = k (S k)
I need continuation and selection to use a higher universe for other reasons. But then the definition of dagger raises an error:
(Set₁ → Set₁) should be a sort, but it isn't when checking that the inferred type of an application
Set₁ → Set₁
matches the expected type _16
In Agda _→_ is the function space not of any category but that of types and
functions. My guess is that you want the following:
dagger : {R : Set} → ∀ X → Selection R X → Continuation R X
dagger X S k = k (S k)
i.e. the function space of indexed types and index-preserving functions.
Alternatively you could make this clear by using the stdlib's
Relation.Unary notion of morphism and write:
open import Relation.Unary
dagger : {R : Set} → Selection R ⊆ Continuation R
dagger S k = k (S k)

Lifting a boolean statement to a proposition

Given a list, a "property" (a function to Bools), and a proof any f xs ≡ true, I want to get a value of Any (λ x → T (f x)) ns, i.e. I want a to convert a proof from any to a proof of Any.
So far, I have been able to sketch the proof, but I am stuck at a seemingly simple statement (please see the second to last line below):
open import Data.Bool
open import Data.Bool.Properties
open import Data.List
open import Data.List.Relation.Unary.Any using (Any; here; there)
open import Data.Unit
open import Relation.Binary.PropositionalEquality
≡→T : ∀ {b : Bool} → b ≡ true → T b
≡→T refl = tt
any-val : ∀ {a} {A : Set a} (f) (ns : List A)
→ any f ns ≡ true
→ Any (λ x → T (f x)) ns
any-val f [] ()
any-val f (n ∷ ns) any-f-⟨n∷ns⟩≡true with f n
... | true = here (≡→T ?)
... | false = there (any-val f ns any-f-⟨n∷ns⟩≡true)
Apparently, I need to prove that f n ≡ true, which should be trivial to prove because we are in the true branch of the with statement f ≡ true.
What is supposed to go in there? What am I understanding incorrectly?
The key is to use the inspect idiom, which I already described thoroughly in another answer here:
`with f x` matches `false`, but cannot construct `f x == false`
This gives the following code, which I cleaned up, including imports:
open import Data.Bool
open import Data.List
open import Data.List.Relation.Unary.Any using (Any; here; there)
open import Data.Unit
open import Relation.Binary.PropositionalEquality
open import Function
≡→T : ∀ {b : Bool} → b ≡ true → T b
≡→T refl = tt
any-val : ∀ {a} {A : Set a}
f (ns : List A) → any f ns ≡ true → Any (T ∘ f) ns
any-val f (x ∷ l) p with f x | inspect f x
... | false | _ = there (any-val f l p)
... | true | [ fx≡true ] = here (≡→T fx≡true)
On a side note, be sure that you have Agda updated to the last version so that you don't have to handle the empty case any-val f [] (), as in your code.

Agda's standard library Data.AVL.Sets containing Data.String as values

I am trying to figure out how to use Agda's standard library implementation of finite sets based on AVL trees in the Data.AVL.Sets module. I was able to do so successfully using ℕ as the values with the following code.
import Data.AVL.Sets
open import Data.Nat.Properties as ℕ
open import Relation.Binary using (module StrictTotalOrder)
open Data.AVL.Sets (StrictTotalOrder.isStrictTotalOrder ℕ.strictTotalOrder)
test = singleton 5
Now I want to achieve the same thing but with Data.String as the values. There doesn't seem to be a corresponding Data.String.Properties module, but Data.String exports strictTotalOrder : StrictTotalOrder _ _ _ which I thought looked appropriate.
However, just strictly replacing the modules according to this assumption fails.
import Data.AVL.Sets
open import Data.String as String
open import Relation.Binary using (module StrictTotalOrder)
open Data.AVL.Sets (StrictTotalOrder.isStrictTotalOrder String.strictTotalOrder)
Produces the error
.Relation.Binary.List.Pointwise.Rel
(StrictTotalOrder._≈_ .Data.Char.strictTotalOrder) (toList x) (toList x₁)
!= x .Relation.Binary.Core.Dummy.≡ x₁ of type Set
when checking that the expression
StrictTotalOrder.isStrictTotalOrder String.strictTotalOrder
has type
Relation.Binary.IsStrictTotalOrder .Relation.Binary.Core.Dummy._≡_
__<__3
which I find difficult to unpack in detail since I have no idea what the Core.Dummy stuff is. It seems that there is some problem with the pointwise definition of the total order for Strings, but I can't figure it out.
If you look at Data.AVL.Sets, you can see that it is parameterised by a strict total order associated to the equivalence relation _≡_ (defined in Relation.Binary.PropositionalEquality):
module Data.AVL.Sets
{k ℓ} {Key : Set k} {_<_ : Rel Key ℓ}
(isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_)
where
Now we can have a look at how the strict total order on Strings is defined. We first convert the Strings to List Chars and then compare them based on the strict lexicographic ordering for lists:
strictTotalOrder =
On.strictTotalOrder
(StrictLex.<-strictTotalOrder Char.strictTotalOrder)
toList
If we dig into the code for StrictLex.<-strictTotalOrder, we can see that the equivalence relation associated to our List of Chars is built using the pointwise lifting Pointwise.isEquivalence of whatever the equivalence relation for Chars is.
But Pointwise.isEquivalence is defined in term of this datatype:
data Rel {a b ℓ} {A : Set a} {B : Set b}
(_∼_ : REL A B ℓ) : List A → List B → Set (a ⊔ b ⊔ ℓ) where
[] : Rel _∼_ [] []
_∷_ : ∀ {x xs y ys} (x∼y : x ∼ y) (xs∼ys : Rel _∼_ xs ys) →
Rel _∼_ (x ∷ xs) (y ∷ ys)
So when Agda expects a strict total order associated to _≡_, we instead provided it with a strict total order associated to Rel _ on toList which has no chance of unifying.
How do we move on from here? Well, you could define your own strict total order on strings. Alternatively, you can try to turn the current one into one where _≡_ is the equivalence used. This is what I am going to do in the rest of this post.
So, I want to reuse an IsStrictTotalOrder R O with a different equivalence relation R′. The trick is to notice that if can transport values from R a b to R′ a b then, I should be fine! So I introduce a notion of RawIso A B which states that we can always transport values from A to B and vice-versa:
record RawIso {ℓ : Level} (A B : Set ℓ) : Set ℓ where
field
push : A → B
pull : B → A
open RawIso public
Then we can prove that RawIsos preserve a lot of properties:
RawIso-IsEquivalence :
{ℓ ℓ′ : Level} {A : Set ℓ} {R R′ : Rel A ℓ′} →
(iso : {a b : A} → RawIso (R a b) (R′ a b)) →
IsEquivalence R → IsEquivalence R′
RawIso-IsEquivalence = ...
RawIso-Trichotomous :
{ℓ ℓ′ ℓ′′ : Level} {A : Set ℓ} {R R′ : Rel A ℓ′} {O : Rel A ℓ′′} →
(iso : {a b : A} → RawIso (R a b) (R′ a b)) →
Trichotomous R O → Trichotomous R′ O
RawIso-Trichotomous = ...
RawIso-Respects₂ :
{ℓ ℓ′ ℓ′′ : Level} {A : Set ℓ} {R R′ : Rel A ℓ′} {O : Rel A ℓ′′} →
(iso : {a b : A} → RawIso (R a b) (R′ a b)) →
O Respects₂ R → O Respects₂ R′
RawIso-Respects₂ = ...
All these lemmas can be combined to prove that given a strict total order, we can build a new one via a RawIso:
RawIso-IsStrictTotalOrder :
{ℓ ℓ′ ℓ′′ : Level} {A : Set ℓ} {R R′ : Rel A ℓ′} {O : Rel A ℓ′′} →
(iso : {a b : A} → RawIso (R a b) (R′ a b)) →
IsStrictTotalOrder R O → IsStrictTotalOrder R′ O
RawIso-IsStrictTotalOrder = ...
Now that we know we can transport strict total orders along these RawIsos, we simply need to prove that the equivalence relation used by the strict total order defined in Data.String is in RawIso with propositional equality. It's (almost) simply a matter of unfolding the definitions. The only problem is that equality on characters is defined by first converting them to natural numbers and then using propositional equality. But the toNat function used has no stated property (compare e.g. to toList and fromList which are stated to be inverses)! I threw in this hack and I think it should be fine but if someone has a better solution, I'd love to know it!
toNat-injective : {c d : Char} → toNat c ≡ toNat d → c ≡ d
toNat-injective {c} pr with toNat c
toNat-injective refl | ._ = trustMe -- probably unsafe
where open import Relation.Binary.PropositionalEquality.TrustMe
Anyway, now that you have this you can unfold the definitions and prove:
rawIso : {a b : String} →
RawIso ((Ptwise.Rel (_≡_ on toNat) on toList) a b) (a ≡ b)
rawIso {a} {b} = record { push = `push ; pull = `pull } where
`push : {a b : String} → (Ptwise.Rel (_≡_ on toNat) on toList) a b → a ≡ b
`push {a} {b} pr =
begin
a ≡⟨ sym (fromList∘toList a) ⟩
fromList (toList a) ≡⟨ cong fromList (aux pr) ⟩
fromList (toList b) ≡⟨ fromList∘toList b ⟩
b
∎ where
aux : {xs ys : List Char} → Ptwise.Rel (_≡_ on toNat) xs ys → xs ≡ ys
aux = Ptwise.rec (λ {xs} {ys} _ → xs ≡ ys)
(cong₂ _∷_ ∘ toNat-injective) refl
`pull : {a b : String} → a ≡ b → (Ptwise.Rel (_≡_ on toNat) on toList) a b
`pull refl = Ptwise.refl refl
Which allows you to
stringSTO : IsStrictTotalOrder _ _
stringSTO = StrictTotalOrder.isStrictTotalOrder String.strictTotalOrder
open Data.AVL.Sets (RawIso-IsStrictTotalOrder rawIso stringSTO)
Phew!
I have uploaded a raw gist so that you can easily access the code, see the imports, etc.

How do I use Agda's implementation of delimited continuations?

We can implement a delimited continuation monad in Agda rather easily.
There is, however, no need to, as the Agda "standard library" has an implementation of a delimited continuation monad. What confuses me about this implementation, though, is the addition of an extra parameter to the DCont type.
DCont : ∀ {i f} {I : Set i} → (I → Set f) → IFun I f
DCont K = DContT K Identity
My question is: why is the extra parameter K there? And how would I use the DContIMonadDCont instance? Can I open it in such a way that I'll get something akin to the below reference implementation in (global) scope?
All my attempts to use it are leading to unsolvable metas.
Reference implementation of delimited continuations not using the Agda "standard library".
DCont : Set → Set → Set → Set
DCont r i a = (a → i) → r
return : ∀ {r a} → a → DCont r r a
return x = λ k → k x
_>>=_ : ∀ {r i j a b} → DCont r i a → (a → DCont i j b) → DCont r j b
c >>= f = λ k → c (λ x → f x k)
join : ∀ {r i j a} → DCont r i (DCont i j a) → DCont r j a
join c = c >>= id
shift : ∀ {r o i j a} → ((a → DCont i i o) → DCont r j j) → DCont r o a
shift f = λ k → f (λ x → λ k′ → k′ (k x)) id
reset : ∀ {r i a} → DCont a i i → DCont r r a
reset a = λ k → k (a id)
Let me answer your second and third questions first. Looking at how DContT is defined:
DContT K M r₂ r₁ a = (a → M (K r₁)) → M (K r₂)
We can recover the requested definition by specifying M = id and K = id (M also has to be a monad, but we have the Identity monad). DCont already fixes M to be id, so we are left with K.
import Category.Monad.Continuation as Cont
open import Function
DCont : Set → Set → Set → Set
DCont = Cont.DCont id
Now, we can open the RawIMonadDCont module provided we have an instance of the corresponding record. And luckily, we do: Category.Monad.Continuation has one such record under the name DContIMonadDCont.
module ContM {ℓ} =
Cont.RawIMonadDCont (Cont.DContIMonadDCont {f = ℓ} id)
And that's it. Let's make sure the required operations are really there:
return : ∀ {r a} → a → DCont r r a
return = ContM.return
_>>=_ : ∀ {r i j a b} → DCont r i a → (a → DCont i j b) → DCont r j b
_>>=_ = ContM._>>=_
join : ∀ {r i j a} → DCont r i (DCont i j a) → DCont r j a
join = ContM.join
shift : ∀ {r o i j a} → ((a → DCont i i o) → DCont r j j) → DCont r o a
shift = ContM.shift
reset : ∀ {r i a} → DCont a i i → DCont r r a
reset = ContM.reset
And indeed, this typechecks. You can also check if the implementation matches. For example, using C-c C-n (normalize) on shift, we get:
λ {.r} {.o} {.i} {.j} {.a} e k → e (λ a f → f (k a)) (λ x → x)
Modulo renaming and some implicit parameters, this is exactly implementation of the shift in your question.
Now the first question. The extra parameter is there to allow additional dependency on the indices. I haven't used delimited continuations in this way, so let me reach for an example somewhere else. Consider this indexed writer:
open import Data.Product
IWriter : {I : Set} (K : I → I → Set) (i j : I) → Set → Set
IWriter K i j A = A × K i j
If we have some sort of indexed monoid, we can write a monad instance for IWriter:
record IMonoid {I : Set} (K : I → I → Set) : Set where
field
ε : ∀ {i} → K i i
_∙_ : ∀ {i j k} → K i j → K j k → K i k
module IWriterMonad {I} {K : I → I → Set} (mon : IMonoid K) where
open IMonoid mon
return : ∀ {A} {i : I} →
A → IWriter K i i A
return a = a , ε
_>>=_ : ∀ {A B} {i j k : I} →
IWriter K i j A → (A → IWriter K j k B) → IWriter K i k B
(a , w₁) >>= f with f a
... | (b , w₂) = b , w₁ ∙ w₂
Now, how is this useful? Imagine you wanted to use the writer to produce a message log or something of the same ilk. With usual boring lists, this is not a problem; but if you wanted to use vectors, you are stuck. How to express that type of the log can change? With the indexed version, you could do something like this:
open import Data.Nat
open import Data.Unit
open import Data.Vec
hiding (_>>=_)
open import Function
K : ℕ → ℕ → Set
K i j = Vec ℕ i → Vec ℕ j
K-m : IMonoid K
K-m = record
{ ε = id
; _∙_ = λ f g → g ∘ f
}
open IWriterMonad K-m
tell : ∀ {i j} → Vec ℕ i → IWriter K j (i + j) ⊤
tell v = _ , _++_ v
test : ∀ {i} → IWriter K i (5 + i) ⊤
test =
tell [] >>= λ _ →
tell (4 ∷ 5 ∷ []) >>= λ _ →
tell (1 ∷ 2 ∷ 3 ∷ [])
Well, that was a lot of (ad-hoc) code to make a point. I haven't given it much thought, so I'm fairly sure there's nicer/more principled approach, but it illustrates that such dependency allows your code to be more expressive.
Now, you could apply the same thing to DCont, for example:
test : Cont.DCont (Vec ℕ) 2 3 ℕ
test c = tail (c 2)
If we apply the definitions, the type reduces to (ℕ → Vec ℕ 3) → Vec ℕ 2. Not very convincing example, I know. But perhaps you can some up with something more useful now that you know what this parameter does.

Resources