
Brian Hulley wrote:
Therefore I humbly submit a call for authors of books/tutorials on IO to come clean and admit the fact that IO completely changes the language from being purely functional into a functional + process calculus language :-)
I agree, but for the "completely changes" part: the really nice thing about Haskell is that the functional and interaction parts are clearly separated, with (almost) no ill effects of the latter on reasoning in the former, and descriptions of interactions being subject to functional evaluation just as any other kind of data structures. (*) Paul Hudak wrote:
As an author of such a book, I'm not willing to do this. Or at least, if we omit concurrency and impure operations such as unsafePerformIO, Haskell is a purely functional, sequential, and deterministic language, whose semantics, including that of IO, can be explained via conventional equational reasoning.
I'm very surprised to hear you say this, and I certainly cannot agree. a language that contains elements that are not best expressed as functions is not "purely functional" anymore, even when its design carefully ensures that it is still pure, and mainly functional, and can be reasoned about equationally. the element that falls outside the remit of functions is the interaction with the runtime context (operating system/other processes/users/external world/..). Haskell's approach to this issue is mostly functional and clearly separates functional part from the part that is "out of its hands": functionally compute an interaction description, have that interaction performed under outside control, have control returned to functional evaluation with a representation of the interaction result, repeat until done. (an informal recipe like this may be even more suitable for learners than either process calculus rules or claims about being purely functional in principle). if you wanted to model that middle part functionally, you'd have to cover all of the external world as well as scheduling. one nice thing about a process calculus style operational semantics is the modular description; you only need to model how Haskell programs fit into the external world, not the external world itself: assuming that world to be modelled in the same style, we need a miniscule amount of process calculus rules to describe the i/o interactions, falling back to functional-only reasoning for the vast majority of the language.
I'm sure that it can also be explained via a suitable process calculus, but that is an overkill -- such calculi are best used for describing non-deterministic / concurrent languages.
using a process calculus framework does not imply that each process has to be non-deterministic / concurrent -- it just makes it easier to show how the "purely functional, sequential and deterministic" evaluation inside a process running Haskell is embedded into and influenced by interactions with the rest of the framework. attempts to ignore that external framework tend to cloud the issues. and as Brian points out, that is more confusing for learners of the language than having to take a tiny bit of process calculus with your mostly functional prescription!-) cheers, claus (*) it is rather dated by now, and certainly not up to the demands of pragmatic Haskell programmers today, but I discussed some of the various functional i/o styles in this way in chapter 3 of http://www.cs.kent.ac.uk/people/staff/cr3/publications/phd.html