10

In the Idris Tutorial a function for filtering vectors is based on dependent pairs.

filter : (a -> Bool) -> Vect n a -> (p ** Vect p a)
filter f [] = (_ ** [])
filter f (x :: xs) with (filter f xs )
  | (_ ** xs') = if (f x) then (_ ** x :: xs') else (_ ** xs')

But why is it necessary to put this in terms of a dependent pair instead of something more direct such as?

filter' : (a -> Bool) -> Vect n a -> Vect p a

In both cases the type of p must be determined, but in my supposed alternative the redundancy of listing p twice is eliminated.

My naive attempts at implementing filter' failed, so I was wondering is there a fundamental reason that it can't be implemented? Or can filter' be implemented, and perhaps filter was just a poor example to showcase dependent pairs in Idris? But if that is the case then in what situations would dependent pairs be useful?

Thanks!

trevor cook
  • 1,531
  • 8
  • 22

3 Answers3

13

The difference between filter and filter' is between existential and universal quantification. If (a -> Bool) -> Vect n a -> Vect p a was the correct type for filter, that would mean filter returns a Vector of length p and the caller can specify what p should be.

Eduardo Pareja Tobes
  • 3,060
  • 1
  • 18
  • 19
Kim Stebel
  • 41,826
  • 12
  • 125
  • 142
  • 4
    And it's obvious that we cannot produce a vector of an *arbitrary* length for an *arbitrary* type. Let us consider the following case: the input vector is `[]` and `a` is an uninhabited type, but that means we cannot produce a vector for a *non-zero* `p`, because we must provide at least one element `x` of type `a`, and the input does *not* provide us with that. So, indeed, `filter'` cannot be implemented in this general setting. – Anton Trunov Aug 14 '17 at 15:18
  • 1
    Thanks Kim, but how does DPair prevent this behavior? – trevor cook Aug 14 '17 at 17:56
  • 3
    A dependent pair is like saying, "there is a p, such that...", but the caller can't specify what p is – Kim Stebel Aug 14 '17 at 18:14
  • 2
    Would it be more accurate to switch the order of "universal" and "existential" to match the order of `filter` and `filter'`? `filter` is existential, correct? – trevor cook Aug 14 '17 at 18:34
11

Kim Stebel's answer is right on the money. Let me just note that this was already discussed on the Idris mailing list back in 2012 (!!):

What raichoo posted there can help clarifying it I think; the real signature of your filter' is

filter' : {p : Nat} -> {n: Nat} -> {a: Type} -> (a -> Bool) -> Vect a n -> Vect a p

from which it should be obvious that this is not what filter should (or even could) do; p actually depends on the predicate and the vector you are filtering, and you can (actually need to) express this using a dependent pair. Note that in the pair (p ** Vect p a), p (and thus Vect p a) implicitly depends on the (unnamed) predicate and vector appearing before it in its signature.

Expanding on this, why a dependent pair? You want to return a vector, but there's no "Vector with unknown length" type; you need a length value for obtaining a Vector type. But then you can just think "OK, I will return a Nat together with a vector with that length". The type of this pair is, unsurprisingly, an example of a dependent pair. In more detail, a dependent pair DPair a P is a type built out of

  1. A type a
  2. A function P: a -> Type

A value of that type DPair a P is a pair of values

  1. x: a
  2. y: P a

At this point I think that is just syntax what might be misleading you. The type p ** Vect p a is DPair Nat (\p => Vect p a); p there is not a parameter for filter or anything like it. All this can be a bit confusing at first; if so, maybe it helps thinking of p ** Vect p a as a substitute for the "Vector with unknown length" type.

Eduardo Pareja Tobes
  • 3,060
  • 1
  • 18
  • 19
  • I now see why `filter'` cannot be, thanks. But I don't think it has been covered here (or in the mailing list) as to why the original definition doesn't suffer the same as my alternative. That is, why isn't there also an implicit `{p:Nat}` in the original definition allowing the caller to specify the return length? I suppose you are getting to this in your last sentence, but I don't quite understand, could you elaborate? – trevor cook Aug 14 '17 at 17:47
  • Sure, added a more detailed explanation about why you need dependent pairs. – Eduardo Pareja Tobes Aug 14 '17 at 23:21
0

Not an answer, but additional context

Idris 1 documentation - https://docs.idris-lang.org/en/latest/tutorial/typesfuns.html#dependent-pairs

Idris 2 documentation - https://idris2.readthedocs.io/en/latest/tutorial/typesfuns.html?highlight=dependent#dependent-pairs

In Idris 2 the dependent pair defined here

and is similar to Exists and Subset but BOTH of it's values are NOT erased at runtime

srghma
  • 4,770
  • 2
  • 38
  • 54