Skip to content
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

Introduce runs #348

Merged
merged 20 commits into from
Nov 29, 2022
Merged

Introduce runs #348

merged 20 commits into from
Nov 29, 2022

Conversation

konnov
Copy link
Contributor

@konnov konnov commented Nov 18, 2022

Closes #344. This PR introduces the concept of a run, which is not explicitly present in TLA+. I kind of skipped the discussions and ADR phase here and went straight to extending the manual and implementing the new operators in the simulator. By doing so, I have iterated several times on the syntax. After all, we have:

  • a new definition qualifier called run, not supported by the effects checker yet.
  • a new mode called Run, similar to the modes Action and Temporal.
  • Three Run operators:
    • A.then(B) is sequential composition of A and then B.
    • i.times(A) A.repeated(i) is sequential composition of A exactly i times.
    • assert(P) tests P and produces a runtime error, if P evaluates to false.

@Kukovec
Copy link
Contributor

Kukovec commented Nov 18, 2022

I kind of skipped the discussions and ADR phase here and went straight to extending the manual and implementing the new operators in the simulator

Rules for thee, but not for me?

@Kukovec
Copy link
Contributor

Kukovec commented Nov 18, 2022

Also, the diff is 2.5 kLOC, can you please make a note as to which files are autogen/trivially changed so I can ignore them?

@konnov
Copy link
Contributor Author

konnov commented Nov 18, 2022

Also, the diff is 2.5 kLOC, can you please make a note as to which files are autogen/trivially changed so I can ignore them?

Feel free to ignore everything in generated but Tnt.g4, which is the grammar.


The following operator is similar to `[A]_x` of TLA+:

```scala
stutter(A, x)
A.stutter(x)
orKeep(A, x)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a scenario where someone wants to stutter over variables that are different from the set of variables being updated by the action A? I can't event understand what that would mean.

If x is always the set of variables being updated by A, WDYT about getting rid of the argument? Then we can call the operators something like optionally(A).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good point. TLA+ has variables in [A]_vars, since there are no explicit assignments there. Factored it out in a separate issue, since the stuttering operators were already in the manual before: #354


The following operator is similar to `<A>_x` of TLA+:

```scala
nostutter(A, x)
A.nostutter(x)
mustChange(A, x)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks like an expectation (like in a testing framework). WDYT of A.changing(x)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we remove variables in the stuttering operator, we should also remove them in this one, as this one is the negation of stuttering.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It is not exactly a negation. [A]_x is defined as A \/ x' = x and <A>_x is defined as A /\ x' # x. This second one I can see being used with a set of vars different then the set of vars being defined by A, as it can constrain A even further.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct. I am just saying that <A>_x was introduced as a dual of [A]_x. You are probably right that it does not make a lot of sense in the presence of effects and the requirement that all variables should be touched.

Copy link
Contributor

Choose a reason for hiding this comment

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

sutter/nostutter have the benefit of being clearly dual. OrKeep and MustChange have no such obvious relation in their names, IMO.

I don't know what motivates the change in names here, but I think we should at least keep the names related. So maybe something like

  • noChanges/changes
  • orKeep / noKeep (I don't like these)

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's postpone this for #354

These operators are not used in specs yet, so there is no rush to pick a perfect name in this PR.

// execute Init and then Next ten times, while asserting n < 5
run run4 = {
Init.then(10.times(all {
assert(n < 5),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you explain the motivation for adding assert? It seems like a way of reporting counterexamples, but I don't think this should be done at the language level.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it looks like the only convenient way of specifying a failing test as a run. The expectation is basically that the run should go through, unless the assertion fails somewhere. I don't like asserts in TLC. We probably can live with asserts in the runs. But it would be great to have something better than asserts.

Comment on lines 125 to 127
{ name: 'then', effect: '(Read[r1] & Update[u1], Read[r2] & Update[u2]) => Read[r1] & Update[r2]' },
{ name: 'times', effect: '(Read[], Read[r] & Update[u]) => Read[r] & Update[r]' },
{ name: 'assert', effect: '(Read[r]) => Read[r]' },
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'll have to update those to produce a Run effect, right? So we can't use those in other types of definitions that are not runs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, exactly. I did not know how to do that :)

Copy link
Contributor

@thpani thpani left a comment

Choose a reason for hiding this comment

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

In the end, we will have a model-checker written in TNT 😂

I've only read the language docs, I like the approach! Haven't reviewed the code.

doc/lang.md Outdated Show resolved Hide resolved
Comment on lines +1688 to +1692
non-negative). If you want to repeat `A` from `i` to `j` times, you can combine
it with `orKeep` as follows:

```scala
i.times(A).then((j - i).times(A.orKeep(vars)))
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to promote this to a closure operator?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was also thinking about doing that, but then what would be the difference between closure and always on finite prefixes?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point :)

doc/lang.md Outdated Show resolved Hide resolved
doc/lang.md Outdated Show resolved Hide resolved
doc/lang.md Outdated Show resolved Hide resolved
doc/lang.md Outdated Show resolved Hide resolved
doc/lang.md Show resolved Hide resolved
doc/lang.md Show resolved Hide resolved
Comment on lines +1674 to +1679
The operator `times` has the following syntax:

```scala
n.times(A)
times(n, A)
```
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this lead to confusion with the set product operator? Maybe repeat is a better name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point. I copied this name from ruby, which reads very nice, e.g., five times A.

Copy link
Contributor Author

@konnov konnov Nov 23, 2022

Choose a reason for hiding this comment

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

I will rename it to A.repeated(n).

Copy link
Contributor

Choose a reason for hiding this comment

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

Are you still planning to make this change? I agree that times could be confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it is called repeated now

tntc/src/effects/builtinSignatures.ts Outdated Show resolved Hide resolved
@Kukovec
Copy link
Contributor

Kukovec commented Nov 21, 2022

Also, like Thomas, I've just read the language part.

@konnov
Copy link
Contributor Author

konnov commented Nov 23, 2022

@shonfeder, for some reason I am getting this error on MacOS (zsh):

npm run integration
...
TAP version 13
0..0
not ok 0 'exit': unknown command type
  ---
  location: |
    line 26
  supported commands:
    - program
    - in
    - out
    - err
    - check
  ---

# FAILED TO PARSE TESTS

@shonfeder
Copy link
Contributor

@konnov is your worktree clean? The mac tests are passing in CI.

@konnov
Copy link
Contributor Author

konnov commented Nov 23, 2022

@konnov is your worktree clean? The mac tests are passing in CI.

Actually, npm install -g txm helped! I had an older version.

@konnov
Copy link
Contributor Author

konnov commented Nov 23, 2022

I think I have addressed the issues, except for stuttering, which is factored out into another issue. Anybody wants to review?

@shonfeder
Copy link
Contributor

shonfeder commented Nov 23, 2022

Maybe these are more helpful debugging ideas:

  • Is the failure happening when running txm cli-tests.md or txm io-cli-tests.md or both?
  • What is the content of the failing file?

Given the error message, I'm wondering if you'll find something like

<!-- !test type ... -->
    ....

@konnov
Copy link
Contributor Author

konnov commented Nov 23, 2022

Maybe these are more helpful debugging ideas:

  • Is the failure happening when running txm cli-tests.md or txm io-cli-tests.md or both?
  • What is the content of the failing file?

Given the error message, I'm wondering if you'll find something like

<!-- !test type ... -->
    ....

It's all good. The older version of txm did not support test exit, which was introduced in io-cli-tests.md.

@shonfeder
Copy link
Contributor

I see! Hopefully there is a way we can pin the version of our dev deps so we can automate away these kinds of issues in the future.

shonfeder pushed a commit that referenced this pull request Nov 23, 2022
This should help avoid friction due to incompatible versions in our
toolchain in the future.

See, e.g., #348 (comment)
@bugarela
Copy link
Collaborator

Weird, I could swear that I had done this before (pinning the version in package.lock so we don't require a global installation). I was actually questioning why it wasn't working, but it seems like my change never made it through main or was reverted somehow.

Thanks for fixing this, @shonfeder!

@konnov
Copy link
Contributor Author

konnov commented Nov 24, 2022

Does anybody have cycles to get this merged? :)

@shonfeder
Copy link
Contributor

Does anybody have cycles to get this merged? :)

I'm reviewing now. Sorry for the delay.

Copy link
Contributor

@shonfeder shonfeder left a comment

Choose a reason for hiding this comment

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

Looks like at least one left-over change you meant to make in response to previous reviews? I also have a few questions and suggestions.

doc/lang.md Outdated Show resolved Hide resolved
doc/lang.md Outdated Show resolved Hide resolved
Comment on lines +1674 to +1679
The operator `times` has the following syntax:

```scala
n.times(A)
times(n, A)
```
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you still planning to make this change? I agree that times could be confusing.

@@ -1646,33 +1770,33 @@ keeping this operator.
to identify assignments in actions. We introduced `Unchanged` only in the
Temporal mode, which is needed for refinements.

#### Stutter
#### OrKeep
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this being renamed? Seems likely to introduce more confusion imo. "Stutter" already has a lot of docs around it in TLA+, and is specific and unusual enough no one is likely to confuse its meaning for something they already know.

But OrKeep could evoke other associations (related to disjunction, maybe intuitions about processing collections etc.) and users will lose the benefit of finding docs on TLA suttering when they search for it.


The following operator is similar to `<A>_x` of TLA+:

```scala
nostutter(A, x)
A.nostutter(x)
mustChange(A, x)
Copy link
Contributor

Choose a reason for hiding this comment

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

sutter/nostutter have the benefit of being clearly dual. OrKeep and MustChange have no such obvious relation in their names, IMO.

I don't know what motivates the change in names here, but I think we should at least keep the names related. So maybe something like

  • noChanges/changes
  • orKeep / noKeep (I don't like these)

?

Comment on lines +39 to +45
run run1 = {
(n <- 1)
.then(n <- 2)
.then(n <- 3)
.then(n <- 6)
.then(n <- 3)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Quite cool.

How soon before users will want to be able to do:

run run1 = {
  n <- 1;
  n <- 2;
  n <- 6;
  n <- 3;
}

:D

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's why we have then :)

tntc/src/runtime/impl/compilerImpl.ts Outdated Show resolved Hide resolved
tntc/src/runtime/impl/compilerImpl.ts Outdated Show resolved Hide resolved
tntc/src/runtime/impl/compilerImpl.ts Outdated Show resolved Hide resolved
@konnov
Copy link
Contributor Author

konnov commented Nov 29, 2022

I think I have implemented all the required changes and introduced a follow-up issue on stuttering operators.

@shonfeder
Copy link
Contributor

Awesome! I can’t wait to play with this feature :)

@konnov konnov merged commit 0b906d2 into main Nov 29, 2022
@konnov konnov deleted the igor/runs344 branch November 29, 2022 17:29
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.

Introduce syntax for runs
5 participants