Q: Is it a good idea to put the bounds way up so there are more potential solutions? A: No, because raising the bounds exponentially increases the solutions Alloy needs to consider.
Hint for the homework, Problem 2: Goats and Wolves - the smallest solution Tim knows of is 11 crossing events (12 states). Also, util/ordering
may be significantly more efficient than seq
for some models, which was something we did not anticipate. If your model is taking an unreasonable time to run (more than a few minutes), try switching to util/ordering
on States.
Farmer, Fox, Chicken, Grain
file -> open sample models -> examples -> puzzles -> farmer
.fact eating { eats = Fox->Chicken + Chicken->Grain }
Two ways of looking at eats:
object1 -> object2 in eats
means that object1 eats object2.For the purpose of experimentation, let’s order all the objects using util/ordering
.
open util/ordering[Object]
In the evaluator, we can now use this ordering. For example, entering
first
outputs
{Chicken$0}
What happens if we enter:
first.next.next.next.next.next
If this were a programming language, we would probably get an error, because this field doesn’t exist. But since this is Alloy, where it isn’t true field access but instead relational joins, this evaluates to:
{}
Also,
ordering/next
is a binary relation that gives you all the “edges” of the ordering.
Q: What would we do if we wanted to only order farmers? A: We could pass farmer instead of object. Q: What if we wanted to order all Animal objects (ruling out the grain)? A: We can add a level to the subtyping: Animal extends Object
, etc.
Should we make Animal abstract? What’s the problem about talking about things that aren’t in a subtype of the abstract sig? Q: What makes abstract different? A: There can’t be direct instances of it. Q: What can you say about the union of the subtypes? A: The union of the subtypes is exactly the abstract sig.
We need to specify initial and ending states in order to get meaningful instances. The built in Alloy solution does this slightly differently than we did in previous classes.
Q: Why is it ord/first
instead of just first
? A: If you’re using multiple modules, this specifies which module’s definitions you intend to use.
Q: What does let
do? A: It allows you to bind an identifier to a certain expression, to avoid repeating that expression in the body of the let statement. (Note: this is a common construct in languages without mutation).
Q: Why is the ending condition a predicate? A: Design decision, if the ending condition is a predicate we can run that predicate to obtain a solution.
If this looks odd to you, Tim suggests opening up this example in Alloy and spending some time looking at the instances it produces.
Let’s look at the fact that models that state transitions are valid. This is different than how we modeled transitions previously, so what’s going on here?
Q: What is the meaning of the expression ord/next[s]
? A: This expression is the next state after s
(of which there is exactly one), or nothing. This is equivalent to s.(ord/next)
. Because there is either one thing or nothing, we could also use a let statement. We don’t need to do “all states but the last one” here, because we are quantifying both s
and s'
. If there is no next state, there will be no s'
, so it won’t trigger the body.
fact stateTransition {
all s: State, s': ord/next[s] {
Farmer in s.near =>
crossRiver[s.near, s'.near, s.far, s'.far] else
crossRiver[s.far, s'.far, s.near, s'.near]
}
}
Note on syntax: =>
here is not implies, because of the else
on the next line. The combination of the two is an if-then-else
block. In Alloy, =>
is an overloaded syntax that can serve either function.
Let’s run the sample instance and check out the evaluator. We used predicates for transitions, so there are no explicit events.
Pros of using util/ordering
instead of seq
: The instances are often easier to read, because atoms are numbered nicely. Projecting over the ordered sig is also much more pleasant.
If something looks fishy/wrong in the evaluator or the tree, use the evaluator to check! In this case: where did the chicken go in this intermediate state?!? It got eaten :(
Q: Why didn’t the chicken simultaneously eat the grain? Who’s to say the fox eats first? A: Interesting, we aren’t sure! Alloy is not imperative, so it’s not that one thing happens first and changes the outcome of the second - is it arbitrarily choosing one? Tim will look into it for Friday!