Is there a way to say that a higher-order function permits preconditions on the function it takes as an argument?
Here's the concrete situation I'm trying to solve. I wrote this function for filtering items in a seq
based on a predicate:
function method filterSeq<T>(s: seq<T>, fn: T -> bool): seq<T>
ensures forall i | 0 <= i < |filterSeq(s, fn)| :: fn(filterSeq(s, fn)[i]) == true
ensures forall i | 0 <= i < |filterSeq(s, fn)| :: filterSeq(s, fn)[i] in s
ensures forall i | 0 <= i < |s| :: fn(s[i]) ==> s[i] in filterSeq(s, fn)
{
if |s| == 0 then [] else (
if fn(s[0]) then [s[0]] + filterSeq(s[1..], fn) else filterSeq(s[1..], fn)
)
}
This covers the postconditions I care about, but doesn't say anything about the argument to fn
. I want to say that for any property which holds for all T
in s
, this property is permitted as a precondition on fn
.
The problem that lead me to this was an attempt to filter a sequence containing other sequences:
var sequences := [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
assert forall i | 0 <= i < |sequences| :: |sequences[i]| == 3;
var result := filterSeq(sequences, sequence => sequence[0] == 4);
When I attempt this, I get an error on sequence[0]
, which says index out of range
. Fair enough, I try to add a precondition to the lambda:
var sequences := [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
assert forall i | 0 <= i < |sequences| :: |sequences[i]| == 3;
var result := filterSeq(sequences,
sequence requires |sequence| == 3 => sequence[0] == 4);
Now I get the error value does not satisfy the subset constraints of 'seq<int> -> bool' (possible cause: it may be partial or have read effects)
on the lambda argument. This also makes sense, I'm passing a partial function where I wrote the type signature for a non-partial one.
My question is: How do I change filterSeq
to allow this? Can I somehow write it so that it will work with arbitrary preconditions, or will I have to write a separate filterSeqOfSequences
method to cover this specific use-case?