Why, restricting code, of course! A subroutine can be a continuation (as it is in non-PFP languages) and have the type-system restrict its actions rather than be modeled as a function (as it is in Haskell).
My definition of pure language is "The language can encode a large class of non-side-effecting computations, and you can tell that they are non-side-effecting from their type". If you are in a non-pure language you cannot distinguish side-effecting from non-side-effecning computations just by looking at their type and thus, working with my definition, it seems impossible to have type-labelled effects in a non-pure language.
The problem is that there are two things here: purity, and functional.
I don't know how to define purity alone, but let's say I take your definition, namely, the language defines a set of operations (that must include IO, and may or may not include mutation) and call them side-effects, and that the compiler enforces the transitive closure of said effects used by a subroutine.
But Haskell employs a very specific design to achieve that, which is pure-functional, i.e., the subroutines in the language must be mathematical functions (or, in Haskell's case, partial functions), and mutation is declared to be a side-effect (which can be said to be a corollary of the first design choice). Neither is required by your definition of purity. A subroutine may be a continuation -- i.e. be able to block and resume -- and still be required to declare its side-effects, and mutation need not at all be considered a side-effect (see, e.g., how synchronous languages are still "referentially transparent" while allowing mutation).
So you like PP (pure programming) but not PFP (pure functional programming)? Fine. For me the functional part is a means but the purity part is an end. If you can explain to me how to achieve purity in a non functional setting I will be extremely happy.
Could you perhaps give me a toy example of a pure imperative language and a type system where effects are handled through continuations? I really don't see how that is possible.
I don't know that I like pure programming; I'm just saying that if that's what people like in PFP, you can get that same sense of purity without PFP. Personally, though, I believe that people who like PFP simply find the paradigm to fit well with how they like to think of programs.
> I believe that people who like PFP simply find the paradigm to fit well with how they like to think of programs.
I don't think so actually, which I why I'm so keen to get to the bottom of your idea.
In my experience people find IO in Haskell very unnatural initially. It certainly took me months to get it. Now that I understand it it causes no additional overhead but I think it would have preferred to avoid it. I would still prefer to program in a more "natural" style and still get the benefits of purity. I just don't believe it's possible.
There seem to be two options
1. Work out how to explain IO more straightforwardly so it consumes less effort to understand it
2. Work out how to get the benefits of pure functional programming in a more natural, imperative setting.