• Nenhum resultado encontrado

Classification Hierarchy

No documento Software Abstractions (páginas 34-39)

2: A Whirlwind Tour

2.3 Classification Hierarchy

In a realistic address book application, you can create an alias for an ad-dress, and then use that alias as the target for another alias. And an alias can name multiple targets, so that a group of addresses can be referred to with a single name.

Rather than elaborating our existing model, we’ll just start afresh and reuse fragments of the old model as needed. We start with a classifica-tion hierarchy showing the various sets of objects and their relaclassifica-tionship to one another:

module tour/addressBook2 abstract sig Target {}

sig Addr extends Target {}

abstract sig Name extends Target {}

sig Alias, Group extends Name {}

sig Book {addr: Name -> Target}

Fig. 2.8 shows a model diagram, a graphical representation of the mod-el’s declarations, generated automatically by the analyzer from the text above. Note that the addr field of Book now maps names to targets. A

fig. 2.8 Model diagram for hierarchical address book.

target is either just an address, as before, or a name itself; names are either groups or aliases.

Just as we did for the simple address book, we can explore the state space with simulation predicates. For example, if ask to see a nonempty book

pred show (b: Book) {some b.addr}

run show for 3 but 1 Book

the analyzer responds with the instance of fig. 2.9, in which an alias is mapped to itself. This is the first simulation we’ve done that clearly reveals a flaw to be remedied. We add a fact—a constraint that’s as-sumed always to hold—stating that, for any book, there is no name that belongs to the set of targets reachable from the name itself:

fact {

all b: Book | no n: Name | n in n.^(b.addr) }

The expression n.^(b.addr) denotes the targets reachable from n, using the transitive closure ^(b.addr) of the address book mapping of b. You can think of x.r as a navigation from object x through one application of relation r, and x.^r as a navigation from object x through one or more applications of r.

Facts like this, that apply to every member of a signature, are better written as signature facts, in which the quantification, and the reference to the particular member, are implicit:

fig. 2.9 First instance for hierarchical address book.

a whirlwind tour 1

sig Book {addr: Name -> Target}

{no n: Name | n in n.^addr}

Note that, like a reference to a field of a receiver in an object-oriented program, addr now implicitly refers to this.addr, the address book map-ping of an archetypal book, and the all quantifier has gone.

Running the command again, we now get a situation, shown in fig.

2.10, in which a group contains two addresses. We’d like to see an alias mapped, so we change the predicate’s constraint to say that there should be some targets resulting from mapping all aliases:

pred show (b: Book) {some Alias.(b.addr)}

Now, in fig. 2.11, we have an alias mapped to two addresses. This is un-desirable; a name mapped to more than one target should be a group, not an alias. So we add another fact:

sig Book {addr: Name -> Target}

{

no n: Name | n in n.^(addr) all a: Alias | lone a.addr }

Executing the command again, we see a new problem, shown in fig. 2.12:

an alias maps to an empty group. This means that if you look up a name, you might get no addresses back at all, even though the name is in the

fig. 2.10 Second instance for hierarchical address book.

address book! In fact, many address book applications allow this, and then (unhelpfully) report a failure only later when the message is sent.

Let’s make this issue explicit in our model. First, we elaborate the Book signature to make explicit the set of names that are in the book, by add-ing a field (names) to represent this set, and by changing the declaration of the address mapping (addr) to say that it maps only names in this set, and maps each to at least one target:

sig Book {

names: set Name,

addr: names -> some Target } {

no n: Name | n in n.^(addr) all a: Alias | lone a.addr }

Then we add an assertion claiming that every lookup of a name in the book yields some results:

assert lookupYields {

all b: Book, n: b.names | some lookup (b,n) }

(We’ll define lookup shortly.) Checking this assertion will give a coun-terexample just like fig. 2.12. The problem isn’t so easy to fix. We could simply add a fact stating, for example, that groups can’t be empty. But

fig. 2.11 Third instance for hierarchical address book.

a whirlwind tour 1

it’s not obvious how to maintain such a property, so we’ll put it off for now and return to it later.

Let’s update the operations to match the new, more elaborate address book:

pred add (b, b’: Book, n: Name, t: Target) {b’.addr = b.addr + n -> t}

pred del (b, b’: Book, n: Name, t: Target) {b’.addr = b.addr - n -> t}

fun lookup (b: Book, n: Name): set Addr {n.^(b.addr) & Addr}

The differences are minor. The add operation now takes a target rather than an address, and del now also takes a target in addition to a name.

At first I didn’t see the need for the second argument of del, but while exploring the model with the analyzer, I realized that without it you wouldn’t be able to remove just one target from a group. The lookup operation is more interesting now, being generalized to arbitrary depth:

it follows the address mapping any number of times, rather than just once, obtaining a set of targets, which it then intersects with the set of addresses, thus returning all addresses reachable from the name.

We can now check the old assertions. The assertion delUndoesAdd (with the extra condition that the name added is not already mapped) still passes, as does addIdempotent. But addLocal now fails, as shown in fig.

2.13. Note the labels indicating which objects act as witnesses to the violation: n’ is Group1, whose associated addresses are changed by an add applied to n, which is Group0. Now that we have indirection, changing

fig. 2.12 Fourth instance for hierarchical address book.

the binding of one alias or group can affect another. This seems reason-able, and we decide that the model doesn’t need to be fixed.

The final version of the model discussed in this section is shown in fig.

2.14.

No documento Software Abstractions (páginas 34-39)