|
Practical TLA+ by Hillel Wayne
Leslie Lamport
Last modified on 23 November 2018
|
|
You'll miss a lot on this web site unless you enable Javascript
in your browser.
The Book
[show]
Practial TLA+ is a book by Hillel Wayne,
available
from the publisher.
It provides a good complete course on the PlusCal algorithm
language.
This web page describes a number of minor problems in the book,
including hard to understand statements and small errors.
It also explains why I disagree with a few things the book says.
I want to emphasize that the issues listed here
are small problems in an excellent book.
General Remarks
[show]
The P Syntax
PlusCal has two syntaxes: the P syntax inspired by Niklaus Wirth's Pascal
programming language, and the C syntax inspired by the programming language
C.
The book uses the P syntax.
That's the syntax I would prefer if I wrote algorithms only
for my own use and never wrote programs.
Most engineers and programmers will prefer the C syntax, which will look
more familiar to them, so I've used it for all my published algorithms.
You should compare the PlusCal manual's P-Version
and C-Version to see which you prefer.
It should be easy to become bi-syntactic, translating the book's
P syntax to C syntax.
Copying the Code
The source files for the specs in the book are available on line.
However, those are the final versions. If you want to follow the
development of the spec in your Toolbox, you'll have to copy the
pieces of the spec from the book. Unfortunately, that removes all
indenting, which makes the result unreadable.
You can try to find the bits of code being added at each step
in the complete spec and copy them from there. It would be nice if
all the code snippets in the book were available in an ascii file,
identified by page and line numbers.
Possible Typos in the Code
I noticed one typo in the code in the book (on page 161, Chapter 10).
It probably occurred in copying text from the PlusCal source file to
the book's source file. I didn't read the code carefully, and
there could be more such errors. If some code in the
book doesn't look right, you should try it yourself to see if the
problem is in your understanding or is a typo in the book.
The Title
[show]
This book is about
PlusCal, not about TLA+.
It describes all the TLA+ operators for writing ordinary mathematical
expressions and says a little about expressing liveness properties
with TLA+'s temporal operators. However, it doesn't mention some
of the most important features of TLA+, including the ability to state
and check that one specification implements a higher-level
specification. After reading this book, you should find it easy
to learn TLA+. I recommend that you do so. While reading
the book, you might want to look at the TLA+ translation of the
PlusCal specs.
Introduction
[show]
- Page xix
-
Without having something to program with, there's really no reason to
use TLA+.
Wrong!
Here are potential users of TLA+ who don't do any programming.
- People who need to express precisely what a system or program
should do, including customers hiring someone else to build the
system or software engineers describing a system for others to
build.
- Computer scientists who design algorithms, but don't write programs
to implement them. I believe I have written only one concurrent
program in my life, which was used by me and a few other people
for a couple of years in the mid-1980s.
Chapter 1
[show]
- Page 7, line 16
-
which is closer to what we normally think of programming functions
This should be something like:
which is closer to what we normally think of in programming as
functions .
- Page 14
-
The text should mention that a state is an assignment of values to variables,
and there is a non-declared variable pc added by the PlusCal to TLA+
translation.
- Page 18, line 5
-
Every following state highlights with variables that have changed
from the previous state.
Better would be:
Every following state highlights with red the variables that have
changed from the previous state.
- Page 22, line 1
-
Unfortunately, there are no good options.
This is not accurate. In particular, the objection of bullet two is
misleading. First of all, it is impossible to write in TLA+ that the
process can't stutter between the withdrawal and deposit.
(You can understand the book without knowing this,
but if you're curious you can look
here
to find out why not.)
The correct statement is that,
by adding the keyword
fair
in front of
process
in the code, we can specify that the process doesn't stop until it's
gotten to the end.
The fact that there's no way to implement it is
irrelevant. It's impossible to implement the safety part of the
spec because a cosmic ray could hit the computer and cause it to do
something not allowed by the spec. Verifying a property shows
that if a behavior satisfies the spec, then it satisfies the
property. This may or may not be useful. Many specs people
write are not implementable--for example, they may contain unbounded
queues, or variables whose value can be an arbitrary integer. It
requires engineering judgment to know whether or not the inability to
implement those things makes it useless to know that the spec
satisfies some property. It's useful to know that the spec with
the fairness assumption satisfies
EventuallyConsistent .
That's because there would be something seriously wrong with the spec
if fairness didn't imply
EventuallyConsistent .
This is not the right place to discuss fairness and
liveness, and that discussion is deferred to Chapter 6 of the book.
However, it would be useful point to that discussion. I think
something like the following belongs here:
For now, the PlusCal specs we write allow processes to stop
at any time. They therefore describe what a system is allowed
to do, but not what it must do. Chapter 6 explains how we
write specs that specify what must happen.
Chapter 2
[show]
- Page 24, line 3
-
This is for backwards compatibility reasons.
I don't know with what this is supposed to be backwards compatible.
- Page 24, line 9
-
It would be a good idea for item (4) to mention that comments can be
nested to arbitrary depth, since, for some unfathomable reason, that's
not true of most programming languages.
- Page 24, lines -5 to -1
-
This paragraph should refer to the tip on the following page,
which explains how to evaluate expressions
without running the entire spec.
- Page 26, lines -10 to -1
-
In the = VS := section,
I don't like the explanation of
=
in the variable declaration as an
initial assignment.
It would be better
to say that a variable's declaration includes its initial value (as an
optional part).
Also, this would be a good point to introduce the distinction between
legal PlusCal specs and ones that TLC can handle. For example,
this is perfectly legal PlusCal:
variables z = (x=y), x = 2, y = 2;
The TLAPlus proof system treats it exactly the same as
variables x = 2, y = 2, z = (x=y);
It's just TLC that can't handle the first.
- Page 27, line 3
-
In introducing the
..
operator, it should be mentioned
that
3..1
equals
{} (the empty set).
- Page 27, line 10
-
All elements in the set must have the same type
This is inaccurate. First,
{"a", 1}
is a legal PlusCal expression; it's TLC that can't deal with it.
Second, being of the same type is a sufficient but not necessary
condition for TLC to be able to deal with an expression. For example,
it has no trouble with
{{1}, {"a", "b"}}
It's usually the case that TLC cannot handle sets containing
elements of different types. However, there's one important
exception: sets of structures. (Structures are introduced on
page 29.) TLC can handle sets of structures whose elements
don't all have the same set of keys.
- Page 29, Note
-
Mathematically, a structure is a function whose
domain is a set of strings, and a sequence is a function whose domain
is a set of integers. I doubt if many people would say that these
kinds of functions have the same type. It would be better to say that
structures and sequences are both instances of a more general class of
values called functions.
- Page 31, line 11
-
For example, the following is a spec error:
This does not mean that the following is incorrect
PlusCal code. It means that an execution of the
spec evaluates the assert statement with
x
equal to
FALSE , generating an
assertion failure.
Formally, a spec is a definition, and a definition can be incorrect
only if it is syntactically illegal. Informally, a definition is
called incorrect if it doesn't define what we think it should.
- Page 32ff
-
In the example, the variable curr can be eliminated by updating
items
at the end of the while loop and replacing occurrences of
curr
by
Head(items) .
Chapter 3
[show]
- Page 44, line 3
-
The TLA+ does not use semicolons; only the PlusCal computations
need semicolons.
This should be:
TLA+ does not use semicolons; only the PlusCal code
that gets translated to TLA+ uses semicolons.
- Page 45, line 15
-
>> Apply(Add, 1, 2)
You can also write this as
Apply(+, 1, 2) ;
there's no need to define
Add or use a
LAMBDA expression.
- Page 46, line 7
-
Not all models you write will check all invariants.
You have to specify what you actually care about.
I believe this means that an invariant is just a formula,
and a spec can define lots of formulas.
TLC has no way of knowing by itself which of those formulas are
supposed to be invariants; you have to tell it in the model.
- Page 47, line 2
-
However, creating dedicated operators is cleaner and better signals
your intent to anybody else who reads your spec.
Defining an operator signals to the reader that the operator is
supposed to be an invariant only if you write in a comment that it's
supposed to be one.
- Page 48 and elsewhere
-
I believe that the quantifiers
\A and
\E are the first TLA+ operators described
so far that declare bound identifiers—for example, the formula
\A num \in set: num < max
declares the bound identifier num
in the scope of the subformula
num < max .
It should be mentioned that in TLA+, it is illegal to
give a meaning to an identifier that already has a meaning.
Thus, the formula above would be illegal if num
had already been defined.
This means that the formula
\A x \in S : (... /\ \E x \in T : ...)
is illegal because the inner
\E assigns a meaning to
x in a context
(the outer \A x )
in which x already has a meaning. Note that
P == \E x \in T : ...
Q == \A x \in S : (... /\ P)
is legal because the use of
x inside the
definition of
P doesn't
leak out of that definition. The rule that
you can't assign a meaning to an identifier that already has a meaning also
makes this illegal
VARIABLE x
...
P(x) == ...
This is also illegal:
\E x \in S : 2*x \in {x \in Nat : ... }
It should be mentioned that the scope of
\A and \E
extends
as far as possible . For example, this is illegal
\A x \in S : x > 1
=>
\E x \in T : x < 23
because it's parsed as
\A x in S : (x > 1 => \E x \in T : x < 23)
The following is legal
(\A x \in S : x > 1) => \E x \in T : x < 23
as is
/\ \A x \in S : x > 1
/\ \E x \in T : x < 23
Extending as far as possible applies to other TLA+ constructs signaled
by a prefix.
- Page 49, line 11
-
\A <<x, y>> \in S \X S:
Op(x, y) = Op(y, x)
Please don't use quantifiers over tuples like this. I think it was a
mistake to allow them in TLA+ because it adds a bit of complexity
for something that's seldom useful. That construct
isn't supported by the TLA+ prover, and probably won't be supported by
new tools. Some day, I expect it no longer to be accepted by the
parser.
- Page 49, line 17
-
People are often confused because
P => Q
is not really equivalent
to the informal use of the phrase if P then Q .
The informal phrase means that
P
being true causes
Q
to be true, while
P => Q
does not imply causality.
- Page 51, line 15
-
make sure your statements are mutually exclusive
If you make all the cases mutually exclusive, there's little
difference between a CASE and a sequence of nested IF/THEN/ELSEs. One
reason for having a CASE statement is to emphasize symmetry that is
hidden by an IF/THEN/ELSE. For example, compare
AbsoluteValue(x) == CASE x >= 0 -> x [] x =< 0 -> -x
with the equivalent IF/THEN/ELSE.
- Page 53, line -7
-
This has five states, three of which are distinct.
This presumably means that TLC reports finding five states, three
of which are distinct.
- Page 53, line -7
-
On execution, TLC will set one of the flags to true while
leaving the other false.
I don't know what this means and I advise not trying to
figure it out. You shouldn't think of TLC setting variables to
values. TLC conceptually computes all possible executions of the
spec, where an execution is a sequence of states and a state is
defined by the values of all the spec's variables. In the course of
computing all executions, TLC computes all reachable states.
- Page 53, line -4
-
You can make a function as an operator. If the operator doesn't
take any arguments, ...
This means that, since a function is an ordinary value, you can
define an operator to equal a function. If the operator takes no
arguments, you can use the special syntax
Op[x \in S] == ...
for the definition. As is suggested by the example on the next
page, this special syntax can be used for recursive definitions.
If the ordinary syntax is used, a recursive function definition must
be preceded by a RECURSIVE statement.
- Pages 55 and 56
-
It should be mentioned that TLC uses the operators
@@ and :>
to represent functions that aren't sequences or records.
- Page 57, line -9
-
The example has an unnecessary bit of complexity. The knapsack
problem is stated as fitting any number of each item into the
knapsack. Since two different items can have the same size and value,
the knapsack problem remains the same if each item appears in the
knapsack at most once. A knapsack then just becomes a set of items,
not a function from items to natural numbers.
Chapter 4
[show]
- Page 68, line 4
-
In general it is safe.
In general here means usually .
The most common case in which it's not safe to use a symmetry set
is when your spec uses the CHOOSE
operator. Read the Toolbox's help page titled
Model Values and Symmetry to find out precisely
when you can use a symmetry set.
- Page 68, line -9
-
In 99% of the cases you work with, you want finite sets.
Here, work with probably means define to be CONSTANTS .
It's not that uncommon to use infinite sets like
Nat
in a spec.
- Page 68, line -5
-
We could also EXTEND Naturals to get the set Nat
Nat
is also defined in the
Integers module;
but
Int
is not defined in the
Naturals
module. Very rarely will there be any reason to
EXTEND
Naturals
instead of
Integers .
- Page 73, line -5ff
-
As the example shows, strings in TLA+ are defined to be sequences of
characters, though the only way to refer to the character
a
is with
an expression like
"abc"[1] .
However, TLC cannot evaluate that expression.
The only operations on sequences that TLC can evaluate on strings are
\o , Tail ,
and Len .
- Page 75, line 4
-
The section on
EXTENDS should say that
the EXTENDS statement must be
the first statement in the module. (It can be preceded only by
comments.)
- Page 75, line 5
-
The module may not have any constants.
That's not true.
Declared CONSTANTS can be
imported by
EXTENDS
just like defined operators.
Chapter 5
[show]
- Page 80, line 1
-
It should be noted that the variable
pc is
not created if each process has the form
process ...
begin label: while TRUE do ... end while ;
end process
and there are no other labels and no
goto
statement in the code. This
kind of spec is often written for distributed algorithms.
Chapter 6
[show]
- Page 99, line -4
-
Most temporal properties, though, are what's called liveness checks.
In my experience, the temporal properties engineers check most often
are invariance properties. Perhaps they are not considered here
to be temporal properties because they are usually not written as
temporal formulas when entered in a TLC model to be checked.
- Page 101, line 16
-
For label
A:
in an unfair process, writing
A:+
will make it weakly fair.
This is incorrect. To make some actions weakly fair, you have to
write
fair process
and write
label:-
for each action
label
that you don't want to be weakly fair. Writing
label:+
in a
fair process
makes action
label
strongly fair. Modifying labels with
+
and
-
in this way allows you to declare some actions unfair,
some weakly fair, and some strongly fair.
- Page 101, line -9
-
You can also make the spec globally fair by writing
--fair algorithm
instead of
--algorithm
Note that this is not always equivalent to making each process fair.
- Page 101, line -2
-
TLC uses a much faster algorithm to evaluate invariants.
It should be noted that TLC uses this faster algorithm no matter which
way you declare the invariant.
- Page 102, line 16
-
The current version of TLC cannot check set membership of a variable
set as part of a property with
<> .
So you can't write...
This is false. (I don't remember TLC ever having this limitation.)
- Page 102, line -7
-
P ~> Q
means that if there is some state where
P
is true, then either
Q
is true either now or in some future state.
This can be more clearly stated as:
P ~> Q
means that for any state in which
P
is true,
Q
must be true in that state or in some later state.
It should also be noted that
P ~> Q
is defined to equal
[](P => <>Q) .
- Page 103, line 14
-
For a finite spec, these mean the same thing...
Here, finite spec and infinite spec mean
terminating spec and nonterminating spec , respectively.
- Page 103, line -2
-
As with
<> ,
TLC cannot check set membership of a variable set
as part of a property with
<>[] ,
or
[]<> .
As with <> ,
this statement is false.
- Page 104, line 10
-
TLC uses a different algorithm for this, which is slower and
is not parallelizable.
This is true now, but we hope to enhance TLC to use a parallel
liveness checking algorithm. However, checking liveness will always
be slower than checking safety.
- Page 105, libe -8
-
Since this is best represented by a check on pc, we need to
place this after the PlusCal translation.
It could instead go in the algorithm's
define section.
- Page 107, line 1
-
TLC can handle this property because the set, Threads ,
is a constant.
Formula
Liveness
is the first one presented in the book that
quantifies a temporal formula--that is, the first formula of the form
\A v \in S : F
or
\E v \in S : F
where
F is a temporal formula
(one containing
[] , <> ,
or ~> ).
In such a formula,
S
must be a constant set.
Chapter 7
[show]
- Page 113, line 9
-
By algorithm , we're assuming that algorithms are code intended to
terminate and produce an output, rather than run forever or
continuously interact with its environment.
This is an idiosynchratic, inconsistent use of the term
algorithm . For example, see Dekker's Algorithm
on page 104.
- Page 114, line 13
-
TLC will create the constant
DefaultInitValue
The constant is created by the PlusCal to TLA+ translation.
The assignment of a model value
to the constant occurs when the Toolbox initializes the model.
If you create the model before translating the algorithm, you will have
to assign a value to that constant when it appears in the TLA+
translation.
- Page 117, line 14ff
-
The
Leftpad algorithm defined in
the book takes
k
steps to add
k
characters. There is a more interesting algorithm that takes
log k
steps. The idea is to use a helper variable
v
that initially equals
c .
At each step, output is either left unchanged or set to
v \o output , and
v
is set to v \o v .
This is a challenging problem that most
programmers will be able to solve only through a rather long process
of trial and error. A really good programmer will be able to get
it right much quicker—on her first try if she's really, really
good. What she would do is define a formula
I(output, v, in_c, in_n, in_str)
that should be true whenever control is at the beginning of the
while
loop. She would then put the statement
assert I(output, v, in_c, in_n, in_str)
at the beginning of the loop body and debug
the definition of I
as she's debugging the algorithm. Doing this requires a
lot of thinking before you write any code. Thinking before coding is
a good thing to do. On a difficult problem, it will save you time and
produce better code.
- Page 121, line -11
-
It may be instructive to define
Pow2(n) recursively, but it's easier to write
Pow2(n) == 2^n
using the integer exponentiation operator
^
defined in the Naturals and
Integers modules.
- Page 123
-
As an exercise, try writing the
binary_search algorithm yourself,
without looking at the book. I'll bet it doesn't work right
on your first
try. If it fails on your first test, instead of continuing
by trial and error, use the same approach I recommended for the more
efficient
Leftpad
algorithm than the one on page 117. Put an
assert
statement at the beginning of the while loop's body asserting
that either
target = seq[i] for some
i in
low..high , or else
target is not in
seq .
Then design your code so that this assertion is not violated.
Chapter 8
[show]
- Page 127, line 4
-
That means we should write data structures as separate modules
that are extended or instantiated in our spec.
This is standard, good advice. It's what you should do if you were
writing a program rather than a spec. It's also what you should do if
you or others want the identical data structure spec—for example, if
it models a data structure that is already implemented in code.
However, specs are simpler and much smaller than programs. Engineers
generally prefer to keep the spec of a data structure in the same
module as the spec of the algorithm that uses it. Copying and pasting
a few lines of data structure spec is no big deal. Moreover, it
encourages them to modify the data structure so it's exactly what
they want for their new spec.
- Page 128, line 9
-
This means using TLC to get
Assert .
This seems to mean importing the TLC module to import the definition
of Assert .
- Page 132, line -14
-
It is probably a mistake to make the definition of
isLinkedList
local, since for an algorithm that is supposed to create linked
lists, one wants to check that it actually does create one. This
can be done by evaluating
isLinkedList ,
without having to evaluate the complete set of all linked lists.
There is one issue that should be discussed in Chapter 8 but isn't.
It's good to define data structures as simply as possible, to make
sure that the definition is correct. However, for using the data
structure in other specs, efficiency of TLC's evaluation of the operators
can be important. For example,
isLinkedList is defined on page 132 to equal
isLinkedList(PointerMap) ==
LET ... IN \E ordering \in all_seqs : ...
If the cardinality of nodes is
n ,
then all_seqs contains n^n elements,
which makes evaluation of
isLInkedList(PointerMap)
inefficient unless
n
is very small.
It's easy to recursively define an operator
startsLL(node,PointerMap) to be true iff
PointerMap is a linked list
starting at
node ,
where
StartsLL
can be evaluated in time polynomial in
n .
A more efficient definition of
isLinkedList is then
IsLinkedList(PointerMap) ==
LET nodes == ... IN \E node \in nodes : startsLL(node, PointerMap)
You can test that the two definitions of
isLinkedList are the same on
a set of nodes small enough so it can be handled by the inefficient
definition. Having two separate definitions helps assure that both
definitions are correct. Often the more efficient definition of
an operator is a more operational recursive one.
Chapter 9
[show]
- Page 137, line 9
-
A state machine is a system with a finite set of internal states
along with a set of transitions between the states.
That's a finite state machine. Not all the state machines
specified in this chapter are finite. For example, in the spec
on page 141, nothing says that the sets Data
and Clients are finite.
The whole idea of PlusCal and TLA+ is to model a system as a state
machine—usually not a finite-state one. TLC is generally run on
a finite-state model of the state machine.
- Page 146, line 5
-
Ghost variables are more accurately called history variables because
they record information about past states. Somewhat surprisingly,
some refinements require a different kind of variable, which doesn't
have to be implemented, called a prophecy variable—a variable
that predicts what
will happen in the future. Such a variable might be needed if a
decision is made in the spec before it needs to be, and that decision
happens later in the refinement.
Chapter 10
[show]
The example of this chapter nicely illustrates that reasonable
liveness properties often can't be satisfied. But you should remember
that liveness itself is just an approximation of the property we
really want. It's of no use to know that a program will
eventually produce an answer if it might take billions of years.
Liveness properties can be useful because checking them can reveal
that the system can't always do something that should be possible.
The properties we really want are real-time properties, which assert
that something must happen within some length of time. These are
safety properties and can be asserted by adding some kind of clock to
the system. It would be a nice exercise to add the requirement
that reservations time out after some number of days have elapsed.
This can be asserted by adding a
date
variable representing the number of days that have elapsed since the
library opened, with an
AdvanceDate
process that repeatedly advances the value of
date by one. A
reservation queue should include the date when an entry was added to
it. A requirement that a user checks out a book before a certain
date can be represented by an enabling condition on when the date can
be advanced.
There's no problem writing PlusCal specs with this kind of real-time
condition, though they might be simpler written directly in TLA+.
However, the number of reachable states in such a spec increases
exponentially with the length of time the system runs. The library
example is simple enough that it should be possible for TLC to check
models that allow time to advance far enough to adequately debug the
code.
Appendix A
[show]
This chapter describes mathematical concepts informally and
imprecisely, the way most
mathematicians do.
To truly understand the concepts, you should understand
how they can be stated rigorously and precisely.
I will try to help you do that.
When a mathematician writes a formula like x+2 > 4 , it can
be a noun as in
We know that if x equals 3 then
x+2 > 4 is true.
or it can be a complete clause, as in
If x equals 3, then x+2 > 4 .
In TLA+, as in all formal mathematics, formulas are nouns. The
assertion that the formula x+2 > 4 is true
is written in TLA+ as
THEOREM x+2 > 4
Whether or not this assertion is true depends on the context in which
it appears. For example, it is true if it appears
in a module and is preceded by
CONSTANT x
ASSUME x \in 3..10
It is false if it's preceded by
x == 2
A rigorous understanding of the mathematical concepts described
in this chapter is obtained by translating the informal assertions
made in the text to theorems, and understanding the context in which
those theorems are true.
- Page 200, line 9
-
Since their columns are different,
A /\ B /= A \/ B .
This is asserting that the following theorem is not true:
THEOREM A /\ B = A \/ B
The assumed context is:
CONSTANTS A, B
ASSUME (A \in BOOLEAN) /\ (B \in BOOLEAN)
(The theorem is true in a context in which
A and B
are Booleans and
A equals B .)
- Page 200, Line 14
-
Since their columns are the same,
A = ¬¬A .
This asserts the truth of:
CONSTANT A
ASSUME A \in BOOLEAN
THEOREM A = ¬¬A
- Page 201, line 10
-
Another way of saying A = B is to write A ⇔ B
The symbol ⇔ is typed
<=>
or
\equiv . It is the Boolean operator
such that for any Boolean values
A and B ,
the expression
A <=> B
equals TRUE if
A equals B
and otherwise equals FALSE .
The semantics of TLA+ do not specify the value of
A <=> B
unless both
A and B
are Booleans. Thus, TLC will evaluate
FALSE <=> TRUE
to equal FALSE , but
will report an error if it tries
to evaluate 42 <=> 27 .
It's good to write
exp1 <=> exp2
rather than
exp1 = exp2
if it appears in a context in which
exp1 and exp2
should both be Boolean-valued expressions, since TLC will
report an error if they're not.
- Page 202, line 10
-
x ∉ {{x}} but {x} ∈ {{x}}.
I believe this is asserting the following two theorems:
THEOREM x ∉ {{x}}
THEOREM {x} ∈ {{x}}
for any value of x . The second
theorem is true. Since {x}
is the one element of
{{x}} ,
the first theorem is true if and only if
x is not equal to
{x} .
But the semantics of TLA+ do not specify whether or not
x equals
{x} ,
so the first theorem is not a true TLA+ theorem.
- Page 203, Line 12
-
TLC can test membership of infinite sets
This means that for some infinite sets
T ,
TLC can test whether a value
v is in
T . Try TLC on these two expressions:
4 \in {x \in Nat : x % 2 = 0}
4 \in {2*x : x \in Nat}
- Page 204, Lines 1 to 5
-
Cardinal numbers like two and seventeen are answers to the
question
how many? . Zero is a cardinal number.
Ordinal numbers like second and seventeenth are answers
to the question which one? . The first ordinal number is
first; there is no number named zeroth. (That's
why the first element of a non-empty sequence
seq is
seq[1] .)
There is no good reason why either kind of number should be considered
more natural than the other.
The standard TLA+ modules define the set
Nat
of natural numbers to contain 0 because that's what computer scientists
do.
- Page 204, Lines 12 to -1
-
I find this discussion rather confusing. The Predicate
Logic section should have contained these theorems,
which are true for all sets
S and Boolean-valued operators
P :
THEOREM (\A x \in S : P(x)) <=> ¬ (\E x \in S : ¬ P(x))
THEOREM (\E x \in S : P(x)) <=> ¬ (\A x \in S : ¬ P(x))
They should help you understand the truth of the following theorems, for any
Boolean-valued operator
P :
THEOREM \A x \in {} : P(x)
THEOREM ¬ (\E x \in {} : P(x))
- Page 206, Line 2
-
TLA+ cannot quantify over infinite sets
This is false. The following is a perfectly legal TLA+ theorem
that the TLAPS proof system easily proves:
THEOREM \A x \in Nat \ {0} : 2*x > x
It is true that TLC cannot evaluate a formula
\A x \in S : ... or
\E x \in S : ...
if S is an infinite set.
Appendix B
[show]
- Page 208, line -10
-
It should be noted that the time and space TLC takes to evaluate
Order(set)
are exponential in the set's cardinality. The obvious
recursive definition would be evaluated more efficiently.
|