As in my previous questions this week, I’m trying to model students joining a course. When a student joins a course, if text-to-speech is enabled for the course, it’ll be enabled for the student. The student can disable TTS afterwards, and that’s fine.
I’m trying to get Alloy to give me an instance of a student that disables TTS by saying “once a student has TTS enabled, it’s enabled forever” and asking it to prove me wrong.
Buuuut, I can’t seem to figure out the proper set of keywords to get this to work. I’ve tried:
check onceYouHaveTTSYouAlwaysHaveTTS {
some s: Student {
once s in TTSStudent always s in TTSStudent
}
}
and
check onceYouHaveTTSYouAlwaysHaveTTS {
some s: Student {
eventually s in TTSStudent implies always s in TTSStudent
}
}
but no luck! In the first case, Alloy shows me an example where the student is never in the TTSStudent
set (which is fine, I suppose, but I don’t care about that until after they’ve been in TTSStudent
for at least a little bit.)
In the second case, Alloy shows me a student who starts off without TTS, then joins a TTS-enabled course, and that’s it. And… that’s somehow a problem? I guess this is also asserting the student is always in TTSStudent
; that’s the only way I can think that this would represent a counterexample.
Here’s my full model:
Full Model
sig Student {}
var sig TTSStudent in Student {}
sig Course {
var roster: set Student
}
var sig TTSCourse in Course {}
pred init {
some Student
some Course
no roster
}
pred stutter {
TTSStudent' = TTSStudent
TTSCourse' = TTSCourse
roster' = roster
}
pred joinCourse [c: Course, s: Student] {
// guard
s not in c.roster
// action
roster' = roster + c -> s
c in TTSCourse
implies TTSStudent' = TTSStudent + s
else TTSStudent' = TTSStudent
// frame
TTSCourse' = TTSCourse
}
pred disableOwnTTS [s: Student] {
// guard
s in TTSStudent
// action
TTSStudent' = TTSStudent - s
// frame
roster' = roster
TTSCourse' = TTSCourse
}
pred allStudentsInACourse {
all s: Student | s in Course.roster
}
pred allCoursesHaveStudents {
all c: Course | some c.roster
}
fact traces {
init
always {
stutter
or (some c: Course, s: Student | joinCourse[c, s])
or (some s: Student | disableOwnTTS[s])
}
}
fact everythingEventuallyJoinsUp {
eventually allStudentsInACourse
eventually allCoursesHaveStudents
}
check ttsIsLeftAloneWhenJoiningANonTTSCourse {
some s: Student, c: Course |
c not in TTSCourse and joinCourse[c, s]
implies TTSStudent' = TTSStudent
}
run {}
check onceYouHaveTTSYouAlwaysHaveTTS {
some s: Student {
once s in TTSStudent always s in TTSStudent
}
}
(Side note: I’d like to know if there’s any way to get Alloy to prefer to make progress wherever it can before stuttering forever. Right now I have the everythingEventuallyJoinsUp
fact, but it feels a little bit like a hack!)