Why would prime (aka ') raise an error in the following: pred add (b, b': Book, n: Name, a: Addr) { b’.addr = b.addr + n -> a }? - alloy

I have Alloy 6.1.0 running on Windows 10
I have "Software Abstractions" text by Daniel Jackson and working through book.
I am at the examples in the "Whirlwind Tour" and section 2.2 "Dynamics:
Adding Operations".
Verbatim from the book I have coded up the following:
module tour/addressBook1
sig Name, Addr {}
sig Book {
addr: Name -> lone Addr
}
pred show (b: Book) {
#b.addr >1
#Name.(b.addr) > 1
}
pred add (b,b': Book, n: Name, a: Addr) {
b'.addr = b.addr + n -> a
}
run add for 3 but 2 Book
Results in:
Syntax error at line 13 column 14:
There are 3 possible tokens that can appear here:
, : =
Against the prime (') in
pred add (b,b': Book, ...
I understand the version of Alloy in the book will be behind the one I am using, but surely prime is still a thing, so it isn't ituitive why I am getting this syntax error.
Is there a module I need load nowadays?
I have also jumped ahead to Figure 2.7 and just snipped the full model out, rather than use my hand coded attempts. Same error at the same point.

Good question, Asterion! As you suspected, the prime symbol is now special in Alloy 6, with the addition of the new temporal features from Electrum. So you'll need to use a different symbol to run the examples from the book. Sorry about this. It's the one cost of the lovely new temporal features.

Related

Alloy pred declaration: is there a difference between square brackets and parentheses?

The question probably has a yes/no answer. Consider the snippet:
sig A { my : lone B }
sig B { }
pred single1 [x:A]{ // defined using []
#x.my = 0
}
pred single2 (x:A){ // defined using ()
#x.my = 0
}
// these two runs produce the exact same results
run single1 for 3 but exactly 1 A
run single2 for 3 but exactly 1 A
check oneOfTheMostTrivialQuestionsOnStackOverflow { all x: A |
single1[x] iff single2[x] // pred calls use [], so as expected, single2(x) would cause a syntax error
} for 3000 but exactly 1 A // assertion holds :)
Are single1 and single2 exactly the same?
They seem to be, but am I missing something?
When we extended the syntax in Alloy 4, we changed the predicate invocations to []. My recollection is that we did it to make parsing easier, so that if you had a predicate P with no args, you could call it as just "P", and there would be no problems if it were followed by a formula in parens "P (...)". As Peter notes, it also seemed reasonable since it's similar to the relational lookup operator, and this makes sense especially for functions. We added the ability to declare predicates and functions with [] for consistency, but saw no reason to prevent () in decls (since there's no possible ambiguity there).
I think the parentheses were originally used for predicates and functions. However, they were changed in favour of the square brackets because it made it look more relational. I vaguely recall that Daniel Jackson explains this in his book.
That said, why ask because you seem to have proven it yourself? :-)

sig literals in Alloy

How can I write out a literal for a sig in Alloy? Consider the example below.
sig Foo { a: Int }
fact { #Foo = 1 }
If I execute this, I get
| this/Foo | a |
|----------|---|
| Foo⁰ | 7 |
In the evaluator, I know I can get a reference to the Foo instance with Foo$0 but how can I write a literal that represents the same value?
I've tried {a: 7}, but this is not equal to Foo$0. This is intentionally a trivial example, but I'm debugging a more complex model and I need to be able to write out literals of sigs with multiple fields.
Ah, this is one of the well hidden secrets! :-) Clearly in your model you cannot refer to atoms since the model is defining all possible values of those atoms. However, quite often you need your hands on some atom to reason about it. That is, you want to be able to name some objects.
The best way to get 'constants' is to create a predicate you call from a run clause. In this predicate, you define names for atoms you want to discuss. You only have to make sure this predicate is true.
pred collision[ car1, car2 : Car, road : Road ] {
// here you can reason about car1 and car2
}
run collision for 10
Another way is to create a quantification whenever you need to have some named objects:
run {
some car1, car2 : Car, road : Road {
// here you can reason about car1 and car2 and road
}
} for 10
There was a recent discussion to add these kinds of instances to the language so that Kodkod could take advantage of them. (It would allow faster solving and it is extremely useful for test cases of your model.) However, during a discussion this solution I presented came forward and it does not require any new syntax.
try to put a limitation for 'Integer' in the 'run' command. I mean :
sig Foo {a : Int}
fact{ #Foo = 1}
pred show {}
run show for 1 Foo, 2 Int

Unexpected Instance found for Alloy specification

I tried my first steps in modelling with Alloy and have come across a problem I cannot get my head around. I have the following spec:
module tour/AddressBook1
sig Name, Addr{}
sig Book {
addr: Name->lone Addr
}
// operation: adds a (name, address) to the Book b, resulting in b'
pred add (b, b' : Book, n:Name, a:Addr) {
b'.addr = b.addr + n->a
}
run add for 3 but 2 Book
So far nothing special. It is literally the example of the book 'Software abstraction' by Daniel Jackson.
It essentially models books, that have (name,address)-pairs associated with it. The add-predicate is supposed to grab a Book-instance b and create another Book-instance b' from it by adding (a potentially already existing) (name,address)-pair to it.
The first few examples found are easy and convincing but clicking through with the Next-button I end up with the following example (full view above, equivalent projection over books below)
I see two books, the starting book Book0 with two pairs (Name0, Addr2) and (Name1, Addr1). From the annotions we can also see that the add operation is going to add the new duo (Name1, Addr2). When I now look at the result book Book1 I have
{(Name0, Addr0),(Name1, Addr2)} instead of
{(Name0, Addr2),(Name1, Addr1),(Name1, Addr2)}
Why is this considered a legal instance?
(Alloy Analyzer 4.2, Build date 2012-09-25 15:54 EDT)
There are two confusing things here:
Book0 does not take part in the operation. Instead Book1 has the role of both b0 and b1.
Adding n->a to a book does not assert that this is a new address. In the instance, Book1 has this address before and after add.
This is because the operation
b'.addr = b.addr + n->a
uses set union. Broken down to a simpler example:
{1, 2} = {1} + {2}
is true. But
{1, 2} = {1, 2} + {2}
is also true. So in the original example, there is nothing that excludes n->a from already being a part of b.addr. And the Alloy Analyzer simply generates all instances that are consistent with the given constraints.
You can improve this example by adding an assertion in add:
b.addr[n] != a

Ordering in Alloy using util/ordering

I am trying to learn about how ordering works in Alloy. I have a time signature which I have used to instantiate the ordering module. I want the predicate addPage to add a page to the book at time t' where t' = t.next. (Basically add a page to the Book on the next time) However it is not working as expected and instead Time2 has lesser number of pages than Time1. Can someone explain to me why this is happening? Thanks.
open util/ordering[Page] as P0
open util/ordering[Time] as T0
sig Page {}
sig Time {}
sig Book
{
pages: Page -> Time
}
pred addPage(b:Book, p:Page, t: Time)
{
t != T0/last implies
{
let t' = t.next |
b.pages.t' = b.pages.t + p
}
}
run addPage {} for 3
The problem are the extra curly braces in the run statement.
I think Alloy executes an empty predicate in this case.
Try:
run addPage for 3
instead. You will see a visualization where the selected instances for b, t and p are marked.
You're trying to change state which can only be simulated in constraint logic.
Please notice that the expression in addPage is basically ineffective /run your model without it/ and that there's only one Book atom in the solution.
Here's a model you can start with and gradually refine.
open util/ordering[Time]
sig Page {}
sig Time {}
sig Book {
pages : Page lone -> Time // each Time atom is mapped to at most one Page atom
}
pred addPage(b0, b1 : Book, pg : Page, t0, t1 : Time) {
one pg and // one page at a time (it's likely redundant)
not pg in b0.pages.Time and // it's a 'new' page
b0.pages + pg->t1 = b1.pages and // 'new state' of b0
t1 = t0.next // pg is 'added' with the next time stamp
}
run addPage for 3 but 2 Book
I used the optional 'and' operators, placed t1 = t0.next at the end of the constraint, positioned b1.pages /representing the 'new state'/ on the right and used quotes in the comments to emphasize that there's no real state change and sequence of operation in the sense imperative programming works.

What's wrong with this Alloy code?

I get a syntax error message for the code below.
The message designates the marked position in the addLocal assertion, stating:
"Syntax error at line 30 column 9: There are 1 possible tokens that can appear here: )"
I just can't see what's wrong here.
abstract sig Target{}
sig Addr extends Target{}
sig Name extends Target{}
sig Book
{
addr: Name->Target
}
pred add(b, b1:Book, n:Name, t:Target)
{
b1.addr = b.addr + (n->t)
}
fun lookup (b: Book, n: Name): set Addr
{
n.^(b.addr) & Addr
}
assert addLocal
{
all
b,b1:Book,
n,n1:Name,
t:Target |
add(b, b1, n, t) and n != n1 => lookup(b, n1) = lookup(b1, n1)
// |- error position
}
For reasons I never quite understood, at some point Alloy's syntax changed from using (or allowing) parentheses around the arguments to predicates and functions, to requiring square brackets. So the relevant line of addLocal needs to be re-punctuated:
add[b, b1, n, t] and n != n1 => lookup[b, n1] = lookup[b1, n1]
I don't have the grammar firmly enough in my head to be sure, but a quick glance at the grammar in appendix B of Software abstractions suggests that parentheses can wrap the arguments in the predicate declaration, but not in a predicate reference; in an expression position, parentheses always wrap a single expression, which would explain why the parser stops when it encounters the first comma in the argument list.

Resources