The problem is that there are only so many concepts one can cover. The course structure, as @justinpombrio notes in another comment above, is that after an assignment comes due, the next class discusses the solutions. Essentially, once I publish all this, I effectively can't use these materials again in a course.
Yes, I know about the problems w/ security through obscurity, etc.
So for now, it's only an in-person course at Brown (and a few other universities that have been picking up this approach).
Sorry, I know, it sucks. But I don't know of a better solution that satisfies all constraints.
If you go to the assignments page, the ones that start with "ML:" are the mystery language assignments. They're online and you can play with them right there!
For the 2017 course [1] we re-implemented the mystery languages in Racket, so you'll need to install DrRacket to use them. But they're a bit improved and more robust.
As @justinpombrio says, I would totally go with the 2017 version instead. Not only is it much better, we are actively supporting and developing this version, so bug reports are actually useful to us and future users.
I’ve seen this sentiment here several times, but I’ve always loved programming. Maybe not every language or project, but no work is perfect all the time. In general, software has been a great career for me.
One problem I've had with students who learn this way is when they hit C and C++, which (unfortunately) must not be learnt this way, as the behaviour of programs in the presence of undefined behaviour is very hard to learn and understand. (I mean here how such programs behave in practice, under GCC or clang).
This isn't a way of learning programming. It's a way of learning about the variation in the language design space.
That includes teaching the very point you're talking about. There is nothing in the mystery language approach that precludes having programs whose behavior is non-deterministic, and this would then show students to prepare for it. Indeed, it's an excellent kind of mystery language, by providing a sandboxed version that can focus precisely on non-determinism.
(I will not reveal whether such behavior exists in the current suite or not, to avoid giving away solutions.)
Yeah, debugging possible outcomes of undefined behavior in C/C++ is pretty much exactly a mystery language exercise.
With older compilers, the usual question is "Will `-2 >> 1` perform sign extension or not?"
With modern compilers, the usual question is "What arbitrary constant can the compiler choose for `-2 >> 1` to maximize the number of provably dead code paths?"
Students who do these assignments spend a lot of time trying to distinguish between these mystery languages, and writing program after program that fails to tell them apart. The next day in class, they hear a simple explanation of how the mystery languages differ. I hope that one of the lessons they walk away with is just how difficult it is to poke at a black box, and a renewed appreciation for specs.
In this case perhaps it would make sense for students to learn Rust first. Much of what is undefined behaviour in C/C++ is a compiler error in Rust (perfect for this approach).
Students can then move on to C/C++ using a different approach, but already having learnt most of the concepts.