
I think this is a useful debate, because it touches on how Haskell meets real-world programming needs, so I shall continue in that spirit... At 22:22 22/11/04 +0000, Keean Schupke wrote:
Obviously without knowing the details I am speculating, but would it not be possible to do a first pass of the XML and build a list of files to read (a pure function) this returns its result to the IO monad where the files are read and concatenated together, and passed to a second (pure functional) processing function. If written well this can take advantage of lazy execution, so both functions end up running concurrently.
In an ideal world, it is certainly possible to separate the pure and non-pure aspects of the code, and do something like you suggest. But my position was that I was working with an existing codebase (HaXml) which had not been structured with this requirement in mind, and I absolutely did not want to start from scratch (as it was, I was forced into some substantial refactoring). This was one case where, in order to get any result at all with the time/effort available to me, I needed to hide the I/OI within an otherwise pure function. Yes, there are better ways but, being a "Bear of Very Little Brain", I have to work with the tools, intellectual and otherwise, that are at my disposal. Most software is not built in the optimum fashion, or even anything close to it. I would suggest that one of the challenges for functional programming is to maker it easy to "do the right thing". I came to functional programming with quite a strong bias to make it work for me, inspired many years ago by John Backus' famous paper, and a presentation by David Turner about KRC, and a few other things. Many programmers I've spoken to who have tried functional programing have given up on it because it's too hard.
It seems to me that as unsafePerformIO is not in the standard and only implemented on some compilers/interpreters, that you limit the portability of code by using it, and that it is best avoided. Also as any safe use of unsafePerformIO can be refactored to not use it I could certainly live without it.
Well, I am concerned about portability. I insist on using Hugs when many people use just GHC, and one of the reasons is that I don't want to get locked into one compiler's extensions. But sometimes it is necessary to use extensions: there are many features of Haskell-98++ that are almost essential (IMO) to practical software development. Including, I think, unsafePerformIO (on rare occasions). My touchstone is that I'll use language extensions when I have to, provided they are supported by both Hugs and GHC. What's my point in all this? I supposed it might be summed up as: "The best is the enemy of the good". #g --
Graham Klyne wrote:
[Switching to Haskell-cafe]
I have used it once, with reservations, but at the time I didn't have the time/energy to find a better solution. (The occasion of its use was accessing external entities within an XML parser; by making the assumption that the external entities do not change within any context in which results from a program are compared, I was able to satisfy the "proof obligation" of not causing or being sensitive to side effects.)
The reason this was important to me is that I wanted to be able to use the parser from code that was not visibly in the IO monad. For me, treating Web data transformations as pure functions is one of the attractions of using Haskell.
(Since doing that, I had an idea that I might be able to parameterize the entity processing code on some Monad, and use either an Identity monad or IO depending on the actual requirements. This way, I could keep pure XML processing out of the IO monad, but use IO when IO was needed.)
In short: I think it's usually possible to avoid using unsafePerformIO, but I'd be reluctant to cede it altogether, if only for sometimes quick-and-dirty pragmatic reasons.
#g
------------ Graham Klyne For email: http://www.ninebynine.org/#Contact
------------ Graham Klyne For email: http://www.ninebynine.org/#Contact