-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: for loops #4
Conversation
``` dafny | ||
var _lo := lo; | ||
var _hi := hi; | ||
assert _lo <= _hi; // this is Dafny-like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does // this is Dafny-like
mean?
Do we really need to assert _lo <= _hi
? This could be needlessly restrictive: I think there are some situations where lo
might already be bigger than hi
, and the intended behavior is simply to do no loop iterations at all if that's the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely agree. It is really useful if the loop simply does nothing when lo > hi
. Note: I understand it will do nothing when lo == hi
, the interval being half-open.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought this would be "Dafny-like" because Dafny already imposes the restriction lo <= hi
in an expression s[lo..hi]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is an example when allowing lo > hi
is useful? Is it a frequent occurrence?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think lo == hi
(which should result in 0 iterations) is very common, and lo > hi
is less common, but here's an example: Say I write a method to resize an array from oldsize
to newsize
(and I don't know which one is bigger), and if newsize
is bigger, the new elements should be initialized to some default
value, so after copying the elements, maybe I want to be "smart" and write a loop like for i in [oldsize..newsize] { newarray[i] = default }
and run that loop no matter whether oldsize
or newsize
is bigger, relying on the fact that if oldsize
is bigger, 0 iterations will be performed. But I guess examples like this one can be easily rewritten so that they also work if lo <= hi
is required by Dafny, I can't think of an example right now where such rewriting would be too cumbersome.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the thoughts. For this particular example, one can also use a forall
statement:
forall i | 0 <= i < newsize {
newarray[i] := if i < oldsize then oldarray[i] else default;
}
Did you consider making all for-loops iterator-based (like Python)?
|
Two central design points are: | ||
- the half-open interval of values for which the loop body is performed (e.g., `0 <= i < a.Length`) and | ||
the closed interval that appears in the loop invariant (e.g., `0 <= i <= a.Length`) | ||
- what should be the possible increments of the loop index (e.g., `+1`, `-1`, `+2`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion, increments other than +1
and -1
are a non-issue. It happens rarely and when it does, we simply use a while
loop.
} | ||
``` | ||
|
||
is still available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI, Why3 has a for each
loop with syntax
for <pat> in <expr> with <Module> do invariant {...} variant {...} <body> done
It roughly desugars to
let it = <Module>.create <expr> in
try while true do invariant {...} variant {...} let <pat> = <Module>.next it in <body> done
with <Module>.Done -> () end
which means that <Module>
is providing
- a function
create
(to create the iterator from<expr>
) - a function
next
(to get the next value) - an exception
Done
(to signal termination of the iteration)
Note: break
and continue
are honored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the way to specify create
and next
, you might be interested in this NFM 2016 paper:
https://hal.inria.fr/hal-01281759v2/document
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason not to use just foreach
loops (using the for
keyword seems fine)?
[lo .. hi]
in for i in [lo .. hi]
is already in Dafny. That'd allow for arbitrary stepping using sequence comprehensions such as in for x in seq(5, i => i*i)
.
The versions with just -1
and +1
updates seem restrictive to me.
@samuelgruetter @backtracking @mschlaipfer I like the idea of also supporting a Dafny has CLU-style iterators. However, they are rarely used, because of some shortcomings. One shortcoming is that they are tricky to specify in general, though simple iterators are fairly easy to specify. Another shortcoming is that iterators don't have a common supertype (other than (The Why3 use of exceptions as part of the The need has also come up for Finally, I don't know what reason Why3 had to have separate
then you'd want something like this: var tmpS := S;
var tmpDone := {};
while tmpDone != tmpS
invariant tmpDone <= tmpS
decreases |tmpS| - |tmpDone|
{
var x :| tmpS - tmpDone;
Body;
tmpDone := tmpDone + {x};
} There probably also needs to be some syntax for mentioning what I here called So, I'm thinking |
Why3 has |
Here's another question. For the most typical
seems straightforward. But what about the
The order of the two bounds (
Here, the lower/upper bounds are written in the same order as in the standard Is the |
I definitely prefer the By the way, here is a good use example of
where Note that, when
|
|
No description provided.