How is this different from just a regular imperative programming language with imperative assignment?
Causal models are just programs (with random inputs, and certain other restrictions if you want to be able to represent them as DAGs). The do() operator is just imperative assignment.
It’s mostly the same as a regular imperative programming language—indeed, that’s largely the point of the post. The do() operator isn’t quite just imperative assignment, though; it has the wrong type-signature for that. It’s more like an operator which creates a subclass on-the-fly, by overriding the getters for a few fields.
How is this different from just a regular imperative programming language with imperative assignment?
Causal models are just programs (with random inputs, and certain other restrictions if you want to be able to represent them as DAGs). The do() operator is just imperative assignment.
It’s mostly the same as a regular imperative programming language—indeed, that’s largely the point of the post. The do() operator isn’t quite just imperative assignment, though; it has the wrong type-signature for that. It’s more like an operator which creates a subclass on-the-fly, by overriding the getters for a few fields.
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.