When I read this kind of thing I sometimes think that what we really want is just for the compiler to provide an AST as a value that we can interpret in code. Then I remember that already exists - it’s called macros.
So what does this style buy us over that? It’s more amenable to testing - you can take the same value and imbue its effects with appropriate semantics in two (or more) different ways, one for test and one for live. You can inspect the intermediate stages (a monad-based programming style often involves several steps of translation, from something highly structured and close to the business language to something less structured and closer to the execution semantics) and compare them. (One big issue I have is that you can’t generally compare functions for equality and therefore you can’t literally compare free monads for value equality - I intend to expand on this in a blog post at some point - but you can at least interpret each level in a test interpreter appropriate to that level). Everything is type-safe.
Is that it? Is that what this is about? Maybe better library facilities around macros could offer the same kind of functionality. Or maybe this is best understood as the natural heir to macros; rather than something that integrates into the compiler, the compiler does the work of treating an AST as a value generically, and then you get to manipulate it “normally”, as a normal value in the language. Or am I missing the point entirely?