In this possible world, it is the case that “A” returns Y upon being given those same observations. But, the output of “A” when given those observations is a fixed computation, so you now need to reason about a possible world that is logically incoherent, given your knowledge that “A” in fact returns X. This possible world is, then, a logical counterfactual: a “possible world” that is logically incoherent.

Simpler solution: in that world, your code is instead A’, which is exactly like A, except that it returns Y in this situation. This is the more general solution derived from Pearl’s account of counterfactuals in domains with a finite number of variables (the “twin network construction”).

Last year, my colleagues and I published a paper on Turing-complete counterfactual models (“causal probabilistic programming”), which details how to do this, and even gives executable code to play with, as well as a formal semantics. Have a look at our predator-prey example, a fully worked example of how to do this “counterfactual world is same except blah” construction.

John is correct that do() is not imperative assignment. It’s a different effect called “lazy dynamic scope.”

do() is described fully in our paper on formal semantics for a language with counterfactuals, http://www.jameskoppel.com/files/papers/causal_neurips2019.pdf . The connection with dynamic scope is covered in the appendix, which is not yet online.