Adding to @Hovercouch's answer: "deleting" does not correspond to what a TLA+ specification means, unlike programming languages.
Declaring dictionary
as a VARIABLE
says that dictionary
is an identifier of an operator that takes no arguments (nullary), and can change value over a behavior ("over time"). It doesn't say anything else.
Looking at step (pair of consecutive states in a behavior), the value of dictionary
(in the first state), and of dictionary'
(of dictionary
in the second state) are unrelated [1, p.313]. Only through constraints expressed in the specification can we restrict their values.
If in a state dictionary = [x \in {"foo", "bar"} |-> x]
, then dictionary'
can be anything (any value in ZF, i.e., any set). See [1, Sec. 6.5 on p.72, Sec. "Don't be..." on p.80, pp.139--140].
The value of the state predicate
dictionary["foo"] = "foo"
at such a state is TRUE
, because indeed dictionary
maps the value "foo"
to the value "foo"
. In contrast, the state predicate:
dictionary["foo"] = "bar"
is FALSE
, because "foo" # "bar"
. If at a step with that state as first one, we write (see also [1, p.82]):
(dictionary["foo"] = "foo")'
we are saying only that the expression dictionary["foo"]
equals "foo"
in the next state. We haven't said that dictionary
is a function in that state---just that it is such a value that dictionary["foo"]
happens to equal "foo"
. Perhaps dictionary
isn't a function there.
What is a function?
When do we call a given value f
a "function"? The answer is when that f
happens to satisfy the formula IsAFunction(f)
, where the operator IsAFunction
is defined as [1, p.303]:
IsAFunction(g) == g = [x \in DOMAIN g |-> g[x]]
Tuples ("arrays") are functions with domain 1..n
for some n \in Nat
. In TLA+, functions are simply values with the above property. Since any value in ZF is a set, functions are sets, in that we can still write y \in f
when f
is a function [1, above Sec. 4.5 on p.43, and Sec 3.3 on p.30].
For example, with the theorem prover TLAPS we can prove the following theorem and check the proof using Isabelle by running tlapm -v -C test.tla
:
---- MODULE test ----
EXTENDS TLAPS
f == [x \in {} |-> x]
g == [x \in {1, 2} |-> 3]
THEOREM
LET
S == f \cup {g}
IN
(1 \in f) => (1 \in S)
PROOF BY Zenon
(* you can replace this with OBVIOUS *)
=====================
(The model checker TLC enumerates states, and because we don't know what elements are contained in a set that happens to be a function, TLC cannot evaluate the expression y \in f
, raising an Error: Attempted to check if the value: ... is an element of the function ...
. The syntactic analyzer SANY confirms that the above module is well-formed.)
We can also write f[x]
when f
isn't a function or when x \notin DOMAIN f
. The problem is that in those cases the value of f[x]
is unspecified, so a specification shouldn't depend on what those values are.
Comments on the specification
The expression dictionary = []
is not part of TLA+. To write the function with empty domain (there is exactly one such function, see the axiom about function extensionality [1, p.303]):
dictionary = [x \in {} |-> TRUE]
I would declare capacity
as a CONSTANT
, because it is intended to remain unchanged over a behavior (unless of course it changes).
Also, currentSize
isn't decreased by the spec, but I assume that this hasn't been added yet.
Also worth noting that the dictionary
will map each item to a unique value, whereas the queue
can end up containing multiple copies of the same item. Not sure what the OP intends for that case.
EXTENDS Integers, Sequences
CONSTANT capacity
VARIABLES currentSize, queue, dictionary
Init == /\ (capacity = 3 ) /\ (currentSize = 0)
/\ (queue = <<>>) /\ (dictionary = [x \in {} |-> TRUE])
AddItem(Item, Value) ==
IF currentSize < capacity
(* Overwrite to what value the dictionary maps
the value. *)
THEN /\ currentSize' = currentSize + 1
/\ queue' = Append(queue, Item)
/\ dictionary' =
[x \in DOMAIN dictionary |->
IF x = Item
THEN Value
ELSE dictionary[x]]
(* Remove the leading item from the dictionary's domain,
then add the given item (perhaps the same), and
map it to the given value. *)
ELSE /\ queue' = Append(Tail(queue), Item)
/\ LET
(* It can happen that OldDom = NewDom. *)
OldDom == DOMAIN queue
first == queue[1]
TailDom == OldDom \ { first }
NewDom == TailDom \cup {Item}
IN
dictionary' =
[x \in NewDom |->
IF x = Item
THEN Value
ELSE dictionary[x]]
GetItem(Item) == dictionary[item]
Next == \/ AddItem
\/ GetItem
The expression
dictionary' = [x \in DOMAIN dictionary |->
IF x = Item THEN Value ELSE dictionary[x]]
can be replaced by [1, p.49]
dictionary' = [dictionary EXCEPT ![Item] = Value]
The expression
dictionary' = [x \in NewDom |->
IF x = Item THEN Value ELSE dictionary[x]]
cannot be replaced with EXCEPT
.
References
[1] Leslie Lamport, "Specifying systems", Addison-Wesley, 2002