Skip to content

Conversation

robin-aws
Copy link
Member

@robin-aws robin-aws commented Jan 24, 2025

What was changed?

See Std/Actions/Actions.md etc.

Also:

How has this been tested?

See ActionsExamples.dfy etc.

By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

@robin-aws robin-aws reopened this Mar 25, 2025
…-streaming-stdlibs

# Conflicts:
#	Source/DafnyCore/Backends/Java/JavaCodeGenerator.cs
#	Source/DafnyStandardLibraries/Makefile
#	Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo
#	Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo
#	Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo
#	Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-js.doo
#	Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-notarget.doo
#	Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo
#	Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo
@robin-aws robin-aws changed the title feat: Actions, Frames, and Termination standard libraries (temp closed to avoid CI) feat: Actions, Frames, and Termination standard libraries Mar 26, 2025
@robin-aws robin-aws changed the title feat: Actions, Frames, and Termination standard libraries feat: Actions, Frames, Termination, and Ordinal standard libraries Mar 31, 2025
Copy link
Collaborator

@RustanLeino RustanLeino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a substantial amount of very nice work. I have some comments, but they can easily be addressed.

nodeCount := left.nodeCount + right.nodeCount + 1;
}

if (right is Node<T>) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for parentheses

}

ghost predicate ValidInput(history: seq<((), Box)>, next: ())
requires ValidHistory(history)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precondition not necessary in this override, but I agree it's good style to include it here. Especially since these examples will serve as a guide to other subtypes.

import opened Std.Wrappers


// Adapting an iterator to a producer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice inclusion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do still want to add an example of enumerating a codatatype, but I'll include that in the next batch of changes to this library.

{
this.iter := new FibonacciIterator();
new;
Repr := {this, iter} + NonNullElements(iter._reads) + NonNullElements(iter._modifies) + NonNullElements(iter._new);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so the type of _reads, _modifies, and _new is set<object?>? That must be legacy from before Dafny had non-null types. Good for now here, but this would be nice (as easy, I imagine) to change in Dafny.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was surprised too. Probably not worth changing by itself, but worth changing if and when we revamp iterators in general.


}

method {:test} FibonnaciExample() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spelling (Fibonacci)

Comment on lines 22 to 23
// TODO: Okay it's now or never - Repr or repr?? :)
ghost var Repr: set<object>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My vote is for Repr. It's an abstraction of something and it is something that public clients need to know about. For example, a class that implements a set would probably declare ghost var Elements: set<T>.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it sounds like you'd like to amend the Dafny style guide to add a clause something like:

"Any fields or constants that public clients may reference are named with PascalCase, and camelCase otherwise. This includes at least all symbols included in at least one export set."

The style guide currently doesn't say anything about fields and non-static constants, and I think most users have inferred that they should always be camelCase. The existing standard library code is already not consistent -for example DynamicArray has:

    ghost var items: seq<A>
    ghost var Repr: set<object>

I'll definitely keep Repr here, but independently we should clarify this point and then go back and align the code better throughout (possibly as part of Dafny 5.0 since we may want change symbols that may already be in use).

Comment on lines 25 to 26
// TODO: Similarly, would Invariant() be better?
ghost predicate Valid()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vote for Valid. The intention is that the condition be invariant, but it describes what it means for the data structure to be valid. Also, it's a bit funny to say that a method "requires and ensures the invariant", since it's the fact that the method "requires and ensures X" that makes X an invariant in the first place.

Comment on lines +21 to +22
ensures forall result' {:trigger} |
forall right' | right' < right :: Plus(left, right') < result' :: result <= result'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that you're using {:trigger} here suggests to me that Dafny doesn't recognize that <= on ORDINAL would be acceptable as a trigger. We should consider extending Dafny to do that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd definitely love to consider adding more axioms and operations on ordinals in the future in general, but only if we think anything besides this module would benefit.

ensures a < b <==> a + 1 <= b

ghost function {:axiom} PlusLimit(left: ORDINAL, right: ORDINAL): (result: ORDINAL)
decreases right, 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually write the decreases clause after requires, reads, modifies, and ensures. If you feel the same, feel free to make that change here and below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record I've actually always preferred this order:

requires ...
reads ...
modifies ...
decreases ...
ensures ...

Mainly because I think of it "chronologically", and it feels like the decreases is relevant "during" the evaluation to ensure termination, and the ensures is relevant "after".

But I do see that the style guide agrees with you so I'm happy to align. :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. That's a decent rule. For the record, here are two other thoughts on this matter:

  • I, too, think of requires as happening before reads, but the language design is actually to allow the two to be mutually dependent. For example,

    class Cell { var data: int }
    function F(a: array<Cell>, i: nat): int
      requires i < a.Length && a[i].data == 10
      reads a, a[i]
  • I think the reason I've placed decreases last is because it feels relevant to the implementation of the function/method, not so much to the caller. This makes sense for a function/method M that is in its own SCC and is itself recursive. But for a caller that's mutually recursive with M, the decreases clause of M is part of the specification that a caller needs to know about.

Comment on lines 79 to 80
// parent is a MaxOrdinal() parameter just to prove termination,
// but doesn't affect the actual result.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment would be good on MaxOrdinal as well.

@robin-aws robin-aws enabled auto-merge (squash) April 7, 2025 21:52
@robin-aws robin-aws merged commit 713a07b into master Apr 7, 2025
22 checks passed
@robin-aws robin-aws deleted the actions-and-streaming-stdlibs branch April 7, 2025 23:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants