Haskell even allows you to define imperative block structures (while, for, ...) as library functions. It isn't very good at it, though.
The "not very good at it" is basically why Haskell has no imperative conditional loops in its standard library. It's one of the surprises that result from monads. You can write functions for while
etc, but they're not very practical. The condition result has to be action (type IO Bool
) since it needs to be sensitive to the loop body side-effects even if it has none of its own. Since IO Bool
and Bool
are two different types, writing the conditions is awkward. It's much easier to use the idiomatic tail call style which is (in readability and maintainability terms) not much different to using goto. Imperative loops are implicit and not visibly obvious, and you can get tail-call spaghetti similar to ye olde goto spaghetti. For it to be a real problem it'd be the programmers style thats at fault - not the language - but you can say that about almost anything.
The problem doesn't arise in a language like C because all conditions (and everything else) are implicitly side-effect-sensitive and potentially side-effecting actions, so all the operators etc are already designed for that context.
Still - the strict separation of side-effecting and non-side-effecting code is a good idea, and the ability to write custom block structures as library functions is interesting. So I think there will be a very good language some time in the future which will steal ideas from (but not be) Haskell. It will have imperative while loops as standard, but it will allow side-effect-sensitive conditions to be written in much the same form as any other condition. It will accept the direct equivalent of not hIsEOF myFileHandle
.
The question is... will it allow you to define your own block structures? Is that even a good idea? It's interesting, but many interesting ideas are actually bad ideas. Personally, I have my doubts.