persistable sclass PStack is Steppable { settable new ProbabilisticScheduler scheduler; settable bool verbose; sclass NoOptionsException extends RuntimeException {} *(VStack.Computable computable) { add(computable); } srecord noeq ExecuteOption(IVF1 option) is VStack.Computable { public void step(VStack stack, O subComputationResult) { O target = stack.caller(); stack.return(); option.get(target); } } class SingleStack extends VStack is IPStack, Runnable { double probability = 1.0; *() {} *(VStack.Computable computable) { super(computable); } run { if (verbose) print("Stepping " + last(stack)); if (step() && !isEmpty()) { if (verbose) print("Re-scheduling at " + probability + ": " + last(stack)); // re-add myself to scheduler to continue scheduler.add(probability, this); } } // give each option the probability 1/n (with n=number of options) public void options(B function, Iterable> options) { L> optionsList = nonNulls(options); if (empty(optionsList)) throw new NoOptionsException; // probability penalty according to number of options probability = probability/l(optionsList); // Schedule all options except first in cloned stacks int n = l(optionsList)-1; for (int i = 0; i < n; i++) { new SingleStack s; s.stack = shallowCloneElements(stack); s.push(new ExecuteOption(optionsList.get(i))); s.probability = probability; scheduler.at(probability, s); } // Last option is executed in same stack push(new ExecuteOption(last(optionsList)); } // give each option its own probability public void probabilisticOptions(B currentFrame, Iterable>> options) { L>> optionsList = nonNulls(options); if (empty(optionsList)) throw new NoOptionsException; // Schedule all options except first in cloned stacks int n = l(optionsList)-1; for (int i = 0; i < n; i++) { new SingleStack s; s.stack = shallowCloneElements(stack); var option = optionsList.get(i); s.push(new ExecuteOption(option!)); s.probability = probability*option.probability(); scheduler.at(s.probability, s); } // Last option is executed in same stack var option = last(optionsList); probability *= option.probability(); push(new ExecuteOption(option!)); } } srecord noeq FollowUp(VStack.Computable computable, IVF1 onCompletion) extends PStackComputableWithStep { // !customConstructor *(VStack.Computable *computable, IVF1 *onCompletion) { probability = getProbability(computable); } void step(IPStack stack) { if (step == 0) { stack.push(computable); ++step; } else { onCompletion.get((A) stack.subResult()); stack.return(); } } } // also takes a PStackComputable void add(VStack.Computable computable) { scheduler.at(getProbability(computable), new SingleStack(computable)); } // also takes a PStackComputable void add(VStack.Computable computable, IVF1 onCompletion) { if (onCompletion != null) add(new FollowUp<>(computable, onCompletion)); else add(computable); } public bool step() { ret scheduler.step(); } meta-for run also as runWithStats { public void run() { scheduler.run(); } void run(VStack.Computable computable) { add(computable); run(); } } static double getProbability(VStack.Computable computable) { ret computable cast PStackComputable ? computable.probability : 1.0; } }