
Greetings Haskellers, I'm a Haskell newbie, and this post began as a scream for help. Having slept on it I find myself thinking of Simon Peyton-Jones' recent request for good use cases. Perhaps a frustrated - and doubting - newbie can also provide a data point. If my worries are unfounded (and I hope they are), I think it's significant that to me, today, they seem real enough. Please understand that I'm not being negative for the sake of it - rather I'm describing what Haskell looks like from the outside. Let me put it this way. Imagine that two weeks ago my forward-thinking and risk-embracing boss asked me to evaluate Haskell for the upcoming Project X. Further imagine that she ensured I was able to sit in the corner emitting curses for the whole two weeks, and on Monday I have to provide my report. At this point, two weeks in, I would be forced to say that I have no reason to believe that Haskell is useful for real world tasks. ghc is an industrial strength compiler for a toy language. While remarkable claims are made for it, in practice even the experts are often unable to implement the most basic behaviours, and where they are able to implement, they find that their program has become so complex that they are unable to describe or discuss the result. Likely this is a deep problem, not a shallow one. The Haskell community is in denial over this, leading to phenomenal time wasting as one goes round and round in circles playing word games with documentation. This risks a return of the chronic embuggerance that we thought we'd escaped when Vista appeared and the set of people who would have to write Windows device drivers reduced to Hewlett Packard employees, Joanna Rutkowska and criminals. When people enthuse about Haskell, we should run a program called Cat.hs from the haskell.org website, throw fruit at them and laugh. Strong words, but in all honesty I *want* to believe, and if I would make such a report I imagine hundreds if not thousands would say the same thing. I'm hoping I'm wrong about this, and what's actually needed is some work on communication (perhaps from a production programming point of view, which I'd be keen to help with). What got me started with Haskell was the video of an Intel employee holding a Teraflops in his hand. I still remember the very silly September 1991 edition of Scientific American, which asked if a Teraflops would *ever* be built. What a stupid question! Stack up enough VIC20s and eventually you'll get a Teraflops. The question should have been "when". Now it's the size of a CD, and only 80 cores are needed. Unfortunately keeping 80 cores running is tricky. I know this from writing some heavy parallel stuff in the mid-90s. It was all quite clever in it's day. Chuck bloated and unguessable CORBA, do something light with TCP/IP (Beuwolf took that to extremes). Neat linkage like rpcgen gave C, so that I could run fast on an SMP Sequent with 30 cores or on a floorfull of about 70 Sun pizza boxen at night. Unfortunately despite having a nice framework, tracing rays is still hard (the rays and medium were... interesting). Making a problem parallel required a sneaky and dedicated person's sincere skull-sweat. Worse, the solutions so produced had a horrible structural instability about them. Just a small change to the requirement could require a computed value where it wasn't needed before, so that it resulted in big changes to the implementation. The skull-sweating would be needed all over again. (Remember that the big point about objects, which e.g. Booch always emphasized, was that a well chosen set of classes maps well to the domain and so reduces such structural instability.) Even then, it was devilish hard to keep 70 "cores" busy. So watching the Intel guy got my klaxons going. We now need to be able to do parallel with ease. Functional programming just got really important. It's years since I last played with Scheme, but I quickly moved on because I could see the "which Scheme" problem becoming a millstone round everyone's necks outside of research contexts. Ditto Lisp. So Haskell. Grown-up compiler, one standard and (apparently) a decent corpus of example code and tutorials. I might be an imperative programmer, but I do lapse - for example I find it very easy to generate swathes of cross referenced documentation using m4. My head goes kind of weird after a few hours, such that m4 seems sane and it's the rest of the world that's ungainly, so maybe it should be banned like DMT, but I like it. I felt able to enter the functional world. I'll omit the first week of suffering, which I see is a well documented rite of passage. (Although most evaluators will have left the building by the end of week one so it's not helping popularity. Perhaps there could be Squires of the Lambda Calculus who haven't done the vigil, mortification of flesh and so on?) Eventually a 3 page introduction on the O'Reilly website together with a good document called "Haskell for C Programmers" got me to the point where I could access "Yet Another Haskell Tutorial", and I was away... for a bit. After a while I'd written some malformed horrors as answers to exercises, and was telling myself that it's probably just an edge effect - deep within a real Haskell program the ugliness would be invisible, tucked away in Ugly.hs. Then I discovered wxHaskell and got very excited. I really like wxWidgets, and I know it well. If I could play with some Haskell which manipulates wxWidgets I'd progress very quickly! I even have a recent C++ wxWidgets program which was annoying me as I wrote it because of the boilerplate. Great! I can play with type inference and mutter "about bloody time" with a smile on my face. Eventually I got a mix of wxWidgets 2.6.3, wxHaskell from darcs, ghc 6.6 running on my Mac, almost exactly as that permutation was described on the website, and I was off. Within a few hours I had a nice frame, with 10 text controls and legends populating it. It already took less lines than C++, and then I discovered how to bundle all the text controls into a tuple to pass them around, and define some "getters" to access the tuple. My line count shrunk to what it "should" be - that is, something Kolmogorov wouldn't laugh at but is unattainable in C++. I had an onOpen which popped up a dialog and got a filename from the user, with all the properties filled in right. I proved that I could update the values in the text controls from the onOpen. Great. Next, translate the bit that says (pseudocode): if(attempt_file_open) if(attempt_file_read) process That's it. No fancy, complex error messages. Just check the error returns and only proceed if I have something to proceed with. Like grown-ups do. I *will* check my error returns. I have tormented too many newbies to *ever* consider doing anything else. If I cannot check my error returns I will not write the program. This is production programming 101, and I suspect that there's a real issue with priorities between the traditional Haskell community and production programmers. So the time of madness began. I could find examples which did: if(attempt_file_open) attempt_file_read process Which is useless. Other examples did: attempt_file_open if(attempt_file_read) process Which is also useless. So I'm looking through the wxHaskell examples. They're all contrived, using very high level functions to (for example) read a complete image structure from a named file, which as one function had one possible source of errors. I go back to scanning and Googling like crazy. Eventually I notice a bit in "Yet Another Haskell Tutorial" - page 65 - which introduces "bracket", immediately explains that it is useless for my (very common) need, and refers the reader to Section 10 - "Advanced Techniques". The author of this excellent document hasn't yet written Section 10. I wonder why. I pause to examine bracket some more. I really can't tell if the "always" clause gets called if the initialization fails. From the use with file opening it looks like it isn't (or the handle to be closed would be invalid) but that wouldn't help if the initializer had two steps, e.g. called socket(2) and bind(2). This is the kind of thing good production programmers get really het up about. I'm still grovelling through reams of stuff, trying to find out how to open *and* read a file in a grown up way, and I find various programs on haskell.org. There's an implementation of cat(1)! That's the thing! A file concatenator must be able to open *and* read it's file. Eagerly I download it. Curiously there doesn't seem to be any error handling. I compile it and point it at a non-existent file. The program crashes. Horribly. So much for Cat.hs. I feel glad I hadn't been able to cope with the video of Simon Peyton-Jones OSCON talk, because the camera operator kept filming his face while he's gesturing at the specific line of code he's talking about with a pointer! After seeing Cat.hs do essence of FAIL just opening a file, claims that Haskell could serve as a scripting language suitable for the crew of the Black Pearl in yonder corner to use would pain me. Now I'm getting cross and turned off the whole business. I've been here before, grovelling through reams of stuff, trying to find something out while each example is contrived to side-step the problem, half-baked and useless, evasive or referencing non-existent documentation. All I need to make the experience complete is a certain firm's trademarks gratuitously embedded in the text at least once on every line. Then I'd be nauseous by now too. Finally I found an uncommented example with no discussion called Hedi. This seems to be doing exception handling, but what it is doing, how, why, what can raise them, so on and so forth would presumably be covered in the "too complex to describe" bit of "Yet Another Haskell Tutorial". I tried to understand it from first principles, looking at the Type theology for the exceptions and various calls, but while I have the cup of tea I lack the necessary Total Perspective Vortex. I felt no confidence trying to even cut and paste it. So question number one: Please - how do I do this? Somehow I doubt that wrapping the complexity of opening a file *and* reading it in a grown up way and then documenting idioms that use the wrapper would help. If it would, the wrapper and doco would already exist. That implies that the complexity doesn't stack. If I get to the point where I can open a file *and* read it in a grown up way, and then I need to (say) verify a version number from the file: if(attempt_file_open) if(attempt_file_read) if(version_ok) process Would I need to completely restructure my program? I suspect so. But it's worth bringing it up. Question number two: Might wrapping the indescribable complexity of the most basic operations make it possible to publish and discuss some idioms which accomplish them? I'd be quite happy with a voodoo idiom for now. I know someone who (20 years on) still doesn't understand pointers to functions. She still uses qsort(3) by rote, and it works for her. That will do for beginners. My biggest worry is that there's a really bad trade-off going on here. Sure, the structural instability of imperative parallel algorithms can be reduced, but at the cost of adding structural instability to everything else! TANSTAAFL and the lump in the carpet always goes somewhere. I read one chap who complained that in order to add one single line of debug he had to completely restructure his entire program, and then the line count was as big as an imperative language would be. Plus a world more complex I bet. It's this TANSTAAFL that's making me a non-believer. So question number three (rhetorical): What would have happened if Codd and Date had tried to make SQL a general purpose programming language? Sequential file handling would have been impossible for all practical purposes, so no-one would have got the benefits of set theory (oh sorry relational calculus) and our most successful intentional language would have been stillborn. We'd still be using CODASYL. Which leads to question number four: Why not do one job well? Limit parallelism to a single SMP machine where there are no comms to fail and failures which do occur will justify the OS chucking the whole process. Make an interface so that Haskell can be called from C++ or Java, with a simple IDL that can marshall nested structs, list<>s, map<>s and vector<>s in and out. Then we can get on with writing ambitious pure computations with names like sortOutRawCatScanData, tersely and in easily parallelizable ways, like we embed SQL to get certain specialist jobs done. Then when all this was going on, question number five appeared: What the hell are these "lightweight Haskell threads"? Are they some kind of green threads running non-preemptively inside a single OS thread? Are they OS threads that could run concurrently on multi-core hardware? If they are green threads (and it sounds like they are) then this is all an academic exercise which has no production application yet. I'm still hoping that this is solvable. That the instinctive priorities of production programmers are just different to those of researchers, and in fact it *is* possible to open a file *and* read it, checking *both* error returns, without being driven insane. If so, I sincerely suggest an example or two, like the small but well formed programs in K&R, Stroustrup or Gosling saying things like: if((fp = fopen(...)) != NULL) { if(fgets(...) != NULL) { printf(...); } fclose(...) } Best wishes - and still hoping I'm wrong after all Alan Carter -- ... the PA system was moaning unctuously, like a lady hippopotamus reading A. E. Housman ..." -- James Blish, "They Shall Have Stars"

On 16/02/2008, Alan Carter
Greetings Haskellers,
I'm still hoping that this is solvable. That the instinctive priorities of production programmers are just different to those of researchers, and in fact it *is* possible to open a file *and* read it, checking *both* error returns, without being driven insane. If so, I sincerely suggest an example or two, like the small but well formed programs in K&R, Stroustrup or Gosling saying things like:
if((fp = fopen(...)) != NULL) { if(fgets(...) != NULL) { printf(...); }
fclose(...) }
Best wishes - and still hoping I'm wrong after all
Alan Carter
Well, first of all, have you read the documentation for System.IO? http://www.haskell.org/ghc/docs/latest/html/libraries/base/System-IO.html That has all the corresponding functions you need. I'm not sure I understand completely how you managed to spend two weeks struggling with this before asking. Two minutes on #haskell, or a quick question about how to open and read a file should have got you a useful response. :) First, I'll write the program in a straightforward, but extremely explicit manner, handling possible errors and managing clean up explicitly. This code is rather verbose, so I'll then show some other less verbose ways to handle things while still maintaining safety. So, the first version: import System.IO import Control.Exception (try) main = do mfh <- try (openFile "myFile" ReadMode) case mfh of Left err -> do putStr "Error opening file for reading: " print err Right fh -> do mline <- try (hGetLine fh) case mline of Left err -> do putStr "Error reading line: " print err hClose fh Right line -> putStrLn ("Read: " ++ line) Okay, so this is hopefully fairly self-explanatory to a C-programmer. The only potentially-confusing part is the function 'try', imported from Control.Exception. What it does is to catch all possible exceptions, and reflect them through the return value of the action. If an exception is thrown, 'try' will catch it, and give us a value of the form (Left e), for e being the exception. If instead, the operation succeeds without an exception, we get a value (Right x), where x is the normal return value of the action. The successive 'case' expressions are used to pattern match on this, and handle the errors by printing out an explanatory message. Some example runs of this program: cale@zaphod:~$ rm myFile cale@zaphod:~$ ./read Error opening file for reading: myFile: openFile: does not exist (No such file or directory) cale@zaphod:~$ touch myFile cale@zaphod:~$ ./read Error reading line: myFile: hGetLine: end of file cale@zaphod:~$ echo "hello" >> myFile cale@zaphod:~$ ./read Read: hello This program actually does more error handling than your example C program, so let's tone it down a bit, and make use of some nice IO operations provided to handle errors and clean things up safely in the event of a failure. import System.IO main = withFile "myFile" ReadMode $ \fh -> do line <- hGetLine fh putStrLn ("Read: " ++ line) The function 'withFile' takes a filename, a mode in which to open the file, and a function, taking a filehandle, and giving an action to be performed with that handle, and wraps that action up inside an exception handler, which ensures that the file handle is safely closed if an exception is thrown. (This doesn't matter much in our small example, but I'm sure you'll appreciate how that's an important thing.) We don't handle the exceptions explicitly in this program, but we still could. There are a host of exception-handling mechanisms in Control.Exception, ranging from simple value-oriented things like try, to more explicit operations for wrapping things in exception handlers, like catch: catch :: IO a -> (Exception -> IO a) -> IO a Or to get more selective: catchJust :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a Which takes a function that gets to decide whether to handle the exception, and at the same time, transform the exception in some way before passing it on to the exception handler. For more information about exceptions, check out the documentation for Control.Exception here: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception... I assure you that Haskell is a very reasonable programming language in which to write safe and correct programs. There are whole companies founded on writing high-assurance software in Haskell. If you have more questions, I would be happy to answer them, either here, or perhaps more comfortably, on IRC, #haskell on irc.freenode.net. It's a very beginner friendly channel, and asking questions there is a great way to learn the language quickly, and find the resources you need. Hope this helps! - Cale

"Cale" == Cale Gibbard
writes:
Cale> So, the first version: Cale> import System.IO import Control.Exception (try) Cale> main = do mfh <- try (openFile "myFile" ReadMode) case mfh Cale> of Left err -> do putStr "Error opening file for reading: " Cale> print err Right fh -> do mline <- try (hGetLine fh) case Cale> mline of Left err -> do putStr "Error reading line: " print Cale> err hClose fh Right line -> putStrLn ("Read: " ++ line) Left? Right? Hardly descriptive terms. Sounds like a sinister language to me. -- Colin Adams Preston Lancashire

Colin Paul Adams wrote:
"Cale" == Cale Gibbard
writes: Cale> So, the first version:
Cale> import System.IO import Control.Exception (try)
Cale> main = do mfh <- try (openFile "myFile" ReadMode) case mfh Cale> of Left err -> do putStr "Error opening file for reading: " Cale> print err Right fh -> do mline <- try (hGetLine fh) case Cale> mline of Left err -> do putStr "Error reading line: " print Cale> err hClose fh Right line -> putStrLn ("Read: " ++ line)
Left? Right?
Hardly descriptive terms. Sounds like a sinister language to me.
I was thinking along the same lines. Politically-sensitive left-handed people everywhere ought to be offended that "Left" is the alternative used to represent errors, mnemonic value notwithstanding. Is there a benefit to reusing a generic Either type for this sort of thing? For code comprehensibility, wouldn't it be better to use more specific names? If I want car and cdr, I know where to find it. Anton

On Sun, 2008-02-17 at 02:46 -0500, Anton van Straaten wrote:
Colin Paul Adams wrote:
> "Cale" == Cale Gibbard
writes: Cale> So, the first version:
Cale> import System.IO import Control.Exception (try)
Cale> main = do mfh <- try (openFile "myFile" ReadMode) case mfh Cale> of Left err -> do putStr "Error opening file for reading: " Cale> print err Right fh -> do mline <- try (hGetLine fh) case Cale> mline of Left err -> do putStr "Error reading line: " print Cale> err hClose fh Right line -> putStrLn ("Read: " ++ line)
Left? Right?
Hardly descriptive terms. Sounds like a sinister language to me.
I was thinking along the same lines. Politically-sensitive left-handed people everywhere ought to be offended that "Left" is the alternative used to represent errors, mnemonic value notwithstanding.
Is there a benefit to reusing a generic Either type for this sort of thing? For code comprehensibility, wouldn't it be better to use more specific names? If I want car and cdr, I know where to find it.
Actually, it's either intentional or ironic that Colin uses the word "sinister" in his response as "Left" is etymologically related to it. See http://en.wikipedia.org/wiki/Left-handed#Negative_associations_of_left-hande... (to the extent wikipedia can be trusted) Indeed, also as wikipedia mentions, there are entire connotations with both words along the lines of how Haskell libraries use them. The benefit of reusing Either is that a) it -is- already mnemonic, b) there are several functions that operate on Eithers in the standard, there's little point in rewriting all of them just so you can say Ok or Error. That said, you often don't see too many explicit uses of the constructors of Either (as functions or patterns) in Haskell code as it is usually more convenient to use combinators (e.g. either or the monad methods) rather than explicit cases.

On 16 Feb 2008, at 11:46 PM, Anton van Straaten wrote:
Colin Paul Adams wrote:
> "Cale" == Cale Gibbard
writes: Cale> So, the first version: Cale> import System.IO import Control.Exception (try) Cale> main = do mfh <- try (openFile "myFile" ReadMode) case mfh Cale> of Left err -> do putStr "Error opening file for reading: " Cale> print err Right fh -> do mline <- try (hGetLine fh) case Cale> mline of Left err -> do putStr "Error reading line: " print Cale> err hClose fh Right line -> putStrLn ("Read: " ++ line) Left? Right? Hardly descriptive terms. Sounds like a sinister language to me. I was thinking along the same lines. Politically-sensitive left- handed people everywhere ought to be offended that "Left" is the alternative used to represent errors, mnemonic value notwithstanding.
Is there a benefit to reusing a generic Either type for this sort of thing?
Standardization. It's already a standard, we need a standard sum type anyway, and it'd be kind of silly to have two isomorphic types with the same signature in the Prelude. jcc

On 17 feb 2008, at 08.46, Anton van Straaten wrote:
Colin Paul Adams wrote:
> "Cale" == Cale Gibbard
writes: Cale> So, the first version: Cale> import System.IO import Control.Exception (try) Cale> main = do mfh <- try (openFile "myFile" ReadMode) case mfh Cale> of Left err -> do putStr "Error opening file for reading: " Cale> print err Right fh -> do mline <- try (hGetLine fh) case Cale> mline of Left err -> do putStr "Error reading line: " print Cale> err hClose fh Right line -> putStrLn ("Read: " ++ line) Left? Right? Hardly descriptive terms. Sounds like a sinister language to me. I was thinking along the same lines. Politically-sensitive left- handed people everywhere ought to be offended that "Left" is the alternative used to represent errors, mnemonic value notwithstanding.
Is there a benefit to reusing a generic Either type for this sort of thing? For code comprehensibility, wouldn't it be better to use more specific names? If I want car and cdr, I know where to find it.
Haskell doesn't have constructor aliases and keeping around dozens of isomorphic types would be stupid. (Views could help, though.) Also, "Right" is naturally used when the everything was alright. It might be arbitrary, but it's not hard to remember - once you're past the newbie phase no-one confuses car and cdr anyways...

On Sun, 17 Feb 2008, Anton van Straaten wrote:
Is there a benefit to reusing a generic Either type for this sort of thing? For code comprehensibility, wouldn't it be better to use more specific names? If I want car and cdr, I know where to find it.
It's Haskell's standard sum type, with a pile of instances already written. There's an instance of MonadError such that you only need to see an Either when you run the computation for example (and then you get an Either whatever the actual error monad was!). If we had appropriate language extensions to map an isomorphic Success/Failure type onto it then I'd probably use them - as it is, the level of inertia around Either is great enough to mean that's only worth doing if I'm expecting to roll a third constructor in at some point. That said, generally I'll wrap it up pretty fast if I have to handle Either directly. Not that that's necessarily any different to cons, car and cdr of course, but there's plenty of library support for doing so. -- flippa@flippac.org "I think you mean Philippa. I believe Phillipa is the one from an alternate universe, who has a beard and programs in BASIC, using only gotos for control flow." -- Anton van Straaten on Lambda the Ultimate

"apfelmus" == apfelmus
writes:
apfelmus> Colin Paul Adams wrote: >> Left? Right? >> >> Hardly descriptive terms. Sounds like a sinister language to >> me. apfelmus> The mnemonics is that Right x is "right" in the sense of apfelmus> correct. So, the error case has to be Left err . As I said, this is sinister (i.e. regarding left-handed people as evil). And left is not the opposite of correct. That would be incorrect. Also, it is not clear to me that a failure to read a file (for instance) is incorrect behaviour. If the file doesn't exist, then I think it ought to be considered correct behaviour to fail to read the file. So Success and Failure seem to be much better. Certainly they make the program far more readable to my eyes. -- Colin Adams Preston Lancashire

On 17 Feb 2008, at 1:12 AM, Colin Paul Adams wrote:
"apfelmus" == apfelmus
writes: apfelmus> Colin Paul Adams wrote:
Left? Right?
Hardly descriptive terms. Sounds like a sinister language to me.
apfelmus> The mnemonics is that Right x is "right" in the sense of apfelmus> correct. So, the error case has to be Left err .
As I said, this is sinister
You do know what `sinister' means, no?
(i.e. regarding left-handed people as evil).
Sheesh, it's just a mnemonic...
And left is not the opposite of correct. That would be incorrect.
No, Left is the opposite of Right. Right is the constructor modified by fmap (due to the design of Haskell type classes); therefore return = Right. Therefore any computation in Either that is not the result of a return is an application of Left.
Also, it is not clear to me that a failure to read a file (for instance) is incorrect behaviour.
Then don't think of Left as `incorrect behavior'. Left isn't incorrect, or Parsec's parse function wouldn't return it on parse errors.
If the file doesn't exist, then I think it ought to be considered correct behaviour to fail to read the file.
So Success and Failure seem to be much better. Certainly they make the program far more readable to my eyes.
But the program succeeded in doing what I expected it to do when if failed... jcc Besides, these decisions were made 15 years ago, they're not going to change now...

On Feb 17, 2008, at 1:12 AM, Colin Paul Adams wrote:
And left is not the opposite of correct. That would be incorrect.
Also, it is not clear to me that a failure to read a file (for instance) is incorrect behaviour. If the file doesn't exist, then I think it ought to be considered correct behaviour to fail to read the file.
Well, of course correct behavior is to cope with both cases in the most appropriate way. If it's any consolation to those of the left handed persuasion, I guessed it wrong - I have used Either in this way, but Left was Success and Right was Failure. I don't enjoy puns, and mapped to an A/B form it seemed obvious that Success is A. Donn Cave, donn@avvanta.com

Donn Cave writes:
On Feb 17, 2008, at 1:12 AM, Colin Paul Adams wrote:
And left is not the opposite of correct. That would be incorrect. ... If it's any consolation to those of the left handed persuasion, I guessed it wrong - I have used Either in this way, but Left was Success and Right was Failure. I don't enjoy puns, and mapped to an A/B form it seemed obvious that Success is A.
Weellll, for those who don't enjoy puns, but feel that the life and everything is one enormous pun, a political reminder. For many years, the world is composed of Leftists and Rightists (let's for the moment forget the normal people). Those from the Left always felt that they were right, and that those from the Right should not be left unpunished, while those from the Right thought that those from the Left should be left to die. Even if it seems right to consider that these deviations should be left to historians, we should not forget that at the beginning of the glorious Soviet country there was a proposal to change the meaning of traffic lights. Red would mean "Forward!!". On the other hand, in France nowadays the difference between Right and Left is more or less the same as between "Immediate failure" and "Delayed failure". Choose yourselves which is which. Jerzy Karczmarczuk

Am Sonntag, 17. Februar 2008 10:12 schrieb Colin Paul Adams:
The mnemonics is that Right x is "right" in the sense of correct. So, the error case has to be Left err .
As I said, this is sinister (i.e. regarding left-handed people as evil).
I hardly can believe that you mean this seriously. Do you really think that the Haskell architects wanted to offend left-handed people? What does assure you that the names of the Either constructors are about handedness? Are you really so sensitive that you want to make people think about all kinds of misinterpretations the usage of an everyday word may cause before they use it? I’d propose that people don’t search for non-existent defamation so that productivity doesn’t get buried under the search for “politically correct” words. Actually, I wouldn’t have dreamed of Left being related to left-handedness. To me, it has long been very clear that Left and Right were assigned its meaning this way round because otherwise you wouldn’t get Functor and Monad instances. A pure technical reason, having nothing to do with hands, politics and whatever you might think of.
[…]
Best wishes, Wolfgang

Alan Carter wrote:
if((fp = fopen(...)) != NULL) { if(fgets(...) != NULL) { printf(...); }
fclose(...) }
This reminds me of a 1976 article written by David Parnas and Harald Würges: Response to undesired events in software systems. Since it's old, it is harder to find, but here are a few things to try: If you have download privilege on ACM Digital Library, http://portal.acm.org/citation.cfm?id=800253.807717 (If not, you can still see the full citation, the abstract, etc.) The paper is also collected in this book full of Parnas's papers: Software Fundamentals: collected papers by David L. Parnas. Edited by Daniel M. Hoffman and David M. Weiss. Someone else made slides to present this paper: http://www.cs.virginia.edu/~wh5a/personal/Quals/misc/ParnasPaper%20on%20stan...

I'm going to try to respond the the main practical question in this message; perhaps others will feel up to addressing the more philosophical aspects. (I see now that Cale has beaten me to the punch, but I guess I'll post this anyways...)
Greetings Haskellers, [snip quite a bit of discussion]
Great. Next, translate the bit that says (pseudocode):
if(attempt_file_open) if(attempt_file_read) process
That's it. No fancy, complex error messages. Just check the error returns and only proceed if I have something to proceed with. Like grown-ups do. I *will* check my error returns. I have tormented too many newbies to *ever* consider doing anything else. If I cannot check my error returns I will not write the program.
You'll find in Haskell that the normal way of handling things like I/O errors is to use the exception handling mechanism. There aren't usually "error returns" to check. Instead you usually place error handlers at the positions where you want to be notified of errors using the "catch" or "handle" functions. If you want to you can convert any IO action into one with an error return by using the "try" function. The Control.Exception module is probably the one you want to check out. http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception... [snip more discussion]
If so, I sincerely suggest an example or two, like the small but well formed programs in K&R, Stroustrup or Gosling saying things like:
if((fp = fopen(...)) != NULL) { if(fgets(...) != NULL) { printf(...); }
fclose(...) }
Here is a quick example I whipped up. It includes both a pretty direct translation of the above code, and another version which is a little more idiomatic. Rob Dockins ----------- code follows ---------------- import Control.Exception import System.IO main = direct_translation direct_translation = do tryh <- try (openFile "test.txt" ReadMode) case tryh of Left err -> print err Right h -> do tryl <- try (hGetLine h) case tryl of Left err -> do print err; hClose h Right l -> do putStrLn l hClose h the_way_I_would_do_it = handle (\err -> print err) $ bracket (openFile "test.txt" ReadMode) hClose $ \h -> do l <- hGetLine h putStrLn l

On Sat, 16 Feb 2008, Alan Carter wrote:
I'm a Haskell newbie, and this post began as a scream for help.
Extremely understandable - to be blunt, I don't really feel that Haskell is ready as a general-purpose production environment unless users are willing to invest considerably more than usual. Not only is it not as "batteries included" as one might like, sometimes it's necessary to build your own batteries! It's also sometimes hard to tell who the experts are, especially as many of us mostly work in fairly restricted areas - often way away from any IO, which is often a source of woe but whose avoidance leaves something of a hole in some coders' expertise. The current state of error-handling is something of a mess, and there are at least two good reasons for this: * Errors originating in the IO monad have a significantly different nature to those generated by pure code * We don't have[1] extensible variants, leading to the kinds of problem you complain about with scalability as the number of potential errors increases It's been a while since I was in the tutorial market, but I don't think many tutorials address the first point properly and it's a biggie. Most IO functions are written to throw exceptions in the IO monad if they fail, which forces you to handle them as such. So, here's an example: import System.IO fileName = "foo.bar" main = (do h <- openFile fileName ReadMode catch (hGetContents h >>= putStr) (\e -> do putStrLn "Error reading file" hClose h ) ) `catch` (\e -> putStrLn "Error opening file") On my machine, putting this through runhaskell results in a line "Error opening file", as unsurprisingly there's no foo.bar. Producing an error opening is harder work, whereas if I change filename to the program's source I get the appropriate output. It may say something about me that I didn't get this to compile first time - the culprit being a layout error, followed by having got the parms to openFile in the wrong order. Caveats so far: there are such things as non-IO exceptions in the IO monad, and catching them requires Control.Error.catch, which thankfully also catches the IO exceptions. If putStr were to throw an exception, I'd need yet another catch statement to distinguish it (though it'd be handled as-is). The sensible thing though is probably to use Control.Error.bracket (which is written in terms of catch) thusly: import System.IO import Control.Error filename = "foo.bar" main = bracket (openFile filename ReadMode) (\h -> hGetContents h >>= putStr) (\h -> hClose h) So from here, we have two remaining problems: 1) What about pure errors? 2) What about new exception types? I'll attack the second first, as there's a standard solution for IO and a similar approach can be adopted in pure code. It's a fairly simple, if arguably unprincipled, solution - use dynamic typing! Control.Error offers us throwDyn and catchDyn, which take advantage of facilities in Data.Dynamic. Pure code can make use of Data.Dynamic in a similar manner if needed. Personally I'm not too happy with this as a solution in most cases, but it's no worse than almost every other language ever - I guess Haskell's capabilities elsewhere have spoiled me. As for pure errors, there're essentially two steps: 1) Find a type that'll encode both the errors and the success cases (Maybe and Either are in common use) 2) Write the appropriate logic I'll not go into step 1 much, most of the time you want to stick with Maybe or Either (there's a punning mnemonic that "if it's Left it can't have gone right" - it's usual to use Right for success and Left for failure). The second point is where you get to adopt any approach from writing out all the case analysis longhand to using a monad or monad transformer based around your error type. It's worth being aware of Control.Monad.Error at this point, though personally I find it a little irritating to work with. By the time you're building customised monads, you're into architecture land - you're constructing an environment for code to run in and defining how that code interfaces with the rest of the world, it's perhaps the closest thing Haskellers have to layers in OO design. If you find you're using multiple monads (I ended up with three in a 300 line lambda calculus interpreter, for example - Parsec, IO and a custom-built evaluation monad) then getting things right at the boundaries is important - if you've got that right and the monad's been well chosen then everything else should come easily. Thankfully, with a little practice it becomes possible to keep your code factored in such a manner that it's easy to refactor your way around the occasional snarl-ups that happen when a new change warrants re-architecting. That or someone just won buzzword bingo, anyway. Anyway, I hope this's been helpful. [1] There are ways of implementing them in GHC, but in practice they're not used enough for anyone to be comfortable building an error-handling library around them -- flippa@flippac.org Society does not owe people jobs. Society owes it to itself to find people jobs.

On Feb 16, 2008, at 3:46 PM, Philippa Cowderoy wrote:
On Sat, 16 Feb 2008, Alan Carter wrote:
I'm a Haskell newbie, and this post began as a scream for help.
Extremely understandable - to be blunt, I don't really feel that Haskell is ready as a general-purpose production environment unless users are willing to invest considerably more than usual. Not only is it not as "batteries included" as one might like, sometimes it's necessary to build your own batteries!
Ironically, the simple task of reading a file is more work than I expect precisely because I don't want to bother to handle exceptions. I mean, in some applications it's perfectly OK to let an exception go to the top. But in Haskell, you cannot read a file line by line without writing an exception handler, because end of file is an exception! as if a file does not normally have an end where the authors of these library functions came from? For the author of the original post ... can't make out what you actually found and tried, so you should know about "catch" in the Prelude, the basic exception handler. Donn Cave, donn@avvanta.com

Donn Cave wrote:
But in Haskell, you cannot read a file line by line without writing an exception handler, because end of file is an exception!
Ah, yet another person who has never found System.IO.hIsEOF :-) Whereas in C or Python you would check the return value of read against zero or an empty string, in Haskell you call hIsEOF *before* a read.

On Sat, Feb 16, 2008 at 05:11:59PM -0800, Bryan O'Sullivan wrote:
Donn Cave wrote:
But in Haskell, you cannot read a file line by line without writing an exception handler, because end of file is an exception!
Ah, yet another person who has never found System.IO.hIsEOF :-)
Whereas in C or Python you would check the return value of read against zero or an empty string, in Haskell you call hIsEOF *before* a read.
I'll bet that breaks horribly in the not-so-corner case of /dev/tty. Stefan

On Sat, Feb 16, 2008 at 06:23:54PM -0800, Bryan O'Sullivan wrote:
Stefan O'Rear wrote:
I'll bet that breaks horribly in the not-so-corner case of /dev/tty.
Actually, it doesn't. It seems to do a read behind the scenes if the buffer is empty, so it blocks until you type something.
Well... that's what I meant by break horribly. Stefan

Bryan O'Sullivan wrote:
Stefan O'Rear wrote:
I'll bet that breaks horribly in the not-so-corner case of /dev/tty.
Actually, it doesn't. It seems to do a read behind the scenes if the buffer is empty, so it blocks until you type something.
Indeed, and this is why even an unbuffered Handle needs to have a 1-character buffer, an interesting fact I discovered when implementing the I/O library. Cheers, Simon

On 16 Feb 2008, at 5:04 PM, Donn Cave wrote:
On Feb 16, 2008, at 3:46 PM, Philippa Cowderoy wrote:
On Sat, 16 Feb 2008, Alan Carter wrote:
I'm a Haskell newbie, and this post began as a scream for help.
Extremely understandable - to be blunt, I don't really feel that Haskell is ready as a general-purpose production environment unless users are willing to invest considerably more than usual. Not only is it not as "batteries included" as one might like, sometimes it's necessary to build your own batteries!
Ironically, the simple task of reading a file is more work than I expect precisely because I don't want to bother to handle exceptions. I mean, in some applications it's perfectly OK to let an exception go to the top.
But in Haskell, you cannot read a file line by line without writing an exception handler, because end of file is an exception! as if a file does not normally have an end where the authors of these library functions came from?
I agree 100%; to make life tolerable around Haskell I/O, I usually end up binding the moral equivalent of tryJust (\ exc -> case exc of IOException e | isEOFError e -> return () _ -> Nothing) $ getLine somewhere at top level and then calling that where it's needed.
For the author of the original post ... can't make out what you actually found and tried, so you should know about "catch" in the Prelude, the basic exception handler.
Also, you might need to know that bracket nests in various ways: bracket openFile hClose $ bracket readLine cleanUpLine $ proceed There's also finally, for when the first argument to bracket is ommitted, and (>>) for when the second argument is :) jcc

On Sat, Feb 16, 2008 at 05:04:53PM -0800, Donn Cave wrote:
But in Haskell, you cannot read a file line by line without writing an exception handler, because end of file is an exception! as if a file does not normally have an end where the authors of these library functions came from?
Part of it is that using 'getLine' is not idiomatic haskell when you don't want to worry about exceptions. Generally you do something like doMyThing xs = print (length xs) main = do contents <- readFile "my.file" mapM_ doMyThing (lines contents) which will call 'doMyThing' on each line of the file, in this case printing the length of each line. or more succinctly: main = readFile "my.file" >>= mapM_ doMyThing . lines John -- John Meacham - ⑆repetae.net⑆john⑈

On 16/02/2008, Alan Carter
Then when all this was going on, question number five appeared: What the hell are these "lightweight Haskell threads"? Are they some kind of green threads running non-preemptively inside a single OS thread? Are they OS threads that could run concurrently on multi-core hardware? If they are green threads (and it sounds like they are) then this is all an academic exercise which has no production application yet.
Best wishes - and still hoping I'm wrong after all
Alan Carter
From this extremely simple form of parallel annotation, it's possible to build lots of interesting mechanisms for carrying out evaluation in
Sorry for missing this question in my first response. The answer of course depends on the Haskell implementation in question, but of course, we're talking about GHC here. Haskell threads, in the sense of Control.Concurrent.forkIO, are essentially a sort of green thread which is scheduled by the Haskell runtime system. Threads can either be bound to a particular OS thread, or (as is default), not be bound to a particular OS thread, allowing the scheduler to manage n Haskell threads with m OS threads, where usually you want to set m to something like the number of processors in your machine. I'm a little hazy on the details, and perhaps someone more familiar with the GHC runtime can fill in some more details for you if you'd like. Aside from Concurrent Haskell (which was originally designed for single-processor concurrency and later extended to allow for scheduling threads to execute in multiple OS threads), there is Parallel Haskell, which is used to annotate pure computations for parallelism (but since they're pure, there is no concurrency). At its core, Parallel Haskell has an extremely simple programmer interface: par :: a -> b -> b Evaluation of an expression of the form (par x y) will cause x to be put in a queue of expressions to be evaluated by a worker on some OS thread, if there is free time, before resulting in y. If there is no time to evaluate x on some processor before it is eventually needed, then evaluation just proceeds normally, but if there is, then it won't need evaluation later, due to the usual sharing from lazy evaluation. parallel. You can read more about that in a paper titled "Algorithm + Strategy = Parallelism" by PW Trinder, K Hammond, H-W Loidl and Simon Peyton Jones, or check out the documentation for Control.Parallel.Strategies. - Cale

On Sat, Feb 16, 2008 at 06:50:03PM -0500, Cale Gibbard wrote:
On 16/02/2008, Alan Carter
wrote: Then when all this was going on, question number five appeared: What the hell are these "lightweight Haskell threads"? Are they some kind of green threads running non-preemptively inside a single OS thread? Are they OS threads that could run concurrently on multi-core hardware? If they are green threads (and it sounds like they are) then this is all an academic exercise which has no production application yet.
Best wishes - and still hoping I'm wrong after all
Alan Carter
Yes, they are green threads. But not the stupid kind you are used to. Consider an operating system. You are running hundreds of threads in a typical system. You don't have hundreds of processors - let's be generous and say you have 8. Obviously these threads are in some sense 'green'. But they are still being run with (limited) parallelism! There is no reason to expect anything less of user-level 'green threads', and if all the systems you have been using are incapable of running threads in paralell, then all the systems you have been using are toys or broken. GHC is not a toy (in this regard), and contains a mini-operating system that schedules how ever many millions of threads you have onto a number of OS threads specified with the +RTS -N<x> option. Stefan

Since everyone's been focusing on the IO so far, I wanted to take a
quick stab at his mention of "green" vs. OS threads... I like the
term "green", actually, as it's what my grandmother calls
decaffeinated coffee, owing to the fact that decaf taster's choice has
a big green plastic lid. Distrust all coffee that comes in a plastic
lid, folks. Life is better that way...
However, Haskell very much has real, caffeinated parallelism
mechanisms. There is explicit concurrency, which says that things can
happen at the same time (see Control.Concurrent) and there is the
whole question of Glasgow Parallel Haskell and Data Parallel Haskell,
which I won't really begin to cover, as Manuel Chakravarty and Simon
Peyton Jones will do TONS better at explaining these than I can. I
will however mention Control.Parallel and Control.Parallel.Strategies,
because they're my personal favorite way of being parallel.
The Haskell thread is semantically much like the Java thread, it's
green, in other words, but you can control the number of real OS
threads that Haskell threads are executed on at the command line.
Thus you might call them "half caffeinated" But, at least with
Control.Parallel.Strategies, they're SO much easier to use. There are
a couple of caveats, but I'll give an example first. Let's say you're
doing some heavy computer graphics, but you're doing it all in
spherical coordinates (I do this all the time, which is why I'm using
it as an example) and before you go to OpenGL, you need to transform
everything into Carteisan coordinates.
vertices :: [GL.Vertex3] -- a list of oh, say, 150,000 vertices or so
in spherical coordinates
sphericalToCart :: GL.Vertex3 -> GL.Vertex3
sphericalToCart (GL.Vertex3 r a z) = (GL.Vertex3 (r * cos a * sin z)
(r * sin a * sin z) (r * cos a))
Now to convert them all, you'd just do a
map sphericalToCart vertices
and that would do a lazy conversion of everything, but since you know
you're going to use all the vertices, strictness is just as well, and
you can do strict things in parallel this way:
parMap rwhnf sphericalToCart vertices
or even more efficiently,
map rwhnf sphericalToCart vertices `using` parListChunk 1024
That'll execute on all cores of your processor and do the same
operation much faster, if you were going to have to do the entire
operation anyway.
-- Jeff
On Sat, Feb 16, 2008 at 5:05 PM, Alan Carter
Greetings Haskellers,
I'm a Haskell newbie, and this post began as a scream for help. Having slept on it I find myself thinking of Simon Peyton-Jones' recent request for good use cases. Perhaps a frustrated - and doubting - newbie can also provide a data point. If my worries are unfounded (and I hope they are), I think it's significant that to me, today, they seem real enough. Please understand that I'm not being negative for the sake of it - rather I'm describing what Haskell looks like from the outside.
Let me put it this way. Imagine that two weeks ago my forward-thinking and risk-embracing boss asked me to evaluate Haskell for the upcoming Project X. Further imagine that she ensured I was able to sit in the corner emitting curses for the whole two weeks, and on Monday I have to provide my report.
At this point, two weeks in, I would be forced to say that I have no reason to believe that Haskell is useful for real world tasks. ghc is an industrial strength compiler for a toy language. While remarkable claims are made for it, in practice even the experts are often unable to implement the most basic behaviours, and where they are able to implement, they find that their program has become so complex that they are unable to describe or discuss the result. Likely this is a deep problem, not a shallow one. The Haskell community is in denial over this, leading to phenomenal time wasting as one goes round and round in circles playing word games with documentation. This risks a return of the chronic embuggerance that we thought we'd escaped when Vista appeared and the set of people who would have to write Windows device drivers reduced to Hewlett Packard employees, Joanna Rutkowska and criminals. When people enthuse about Haskell, we should run a program called Cat.hs from the haskell.org website, throw fruit at them and laugh.
Strong words, but in all honesty I *want* to believe, and if I would make such a report I imagine hundreds if not thousands would say the same thing. I'm hoping I'm wrong about this, and what's actually needed is some work on communication (perhaps from a production programming point of view, which I'd be keen to help with).
What got me started with Haskell was the video of an Intel employee holding a Teraflops in his hand. I still remember the very silly September 1991 edition of Scientific American, which asked if a Teraflops would *ever* be built. What a stupid question! Stack up enough VIC20s and eventually you'll get a Teraflops. The question should have been "when". Now it's the size of a CD, and only 80 cores are needed. Unfortunately keeping 80 cores running is tricky. I know this from writing some heavy parallel stuff in the mid-90s. It was all quite clever in it's day. Chuck bloated and unguessable CORBA, do something light with TCP/IP (Beuwolf took that to extremes). Neat linkage like rpcgen gave C, so that I could run fast on an SMP Sequent with 30 cores or on a floorfull of about 70 Sun pizza boxen at night.
Unfortunately despite having a nice framework, tracing rays is still hard (the rays and medium were... interesting). Making a problem parallel required a sneaky and dedicated person's sincere skull-sweat. Worse, the solutions so produced had a horrible structural instability about them. Just a small change to the requirement could require a computed value where it wasn't needed before, so that it resulted in big changes to the implementation. The skull-sweating would be needed all over again. (Remember that the big point about objects, which e.g. Booch always emphasized, was that a well chosen set of classes maps well to the domain and so reduces such structural instability.) Even then, it was devilish hard to keep 70 "cores" busy.
So watching the Intel guy got my klaxons going. We now need to be able to do parallel with ease. Functional programming just got really important. It's years since I last played with Scheme, but I quickly moved on because I could see the "which Scheme" problem becoming a millstone round everyone's necks outside of research contexts. Ditto Lisp. So Haskell. Grown-up compiler, one standard and (apparently) a decent corpus of example code and tutorials. I might be an imperative programmer, but I do lapse - for example I find it very easy to generate swathes of cross referenced documentation using m4. My head goes kind of weird after a few hours, such that m4 seems sane and it's the rest of the world that's ungainly, so maybe it should be banned like DMT, but I like it. I felt able to enter the functional world.
I'll omit the first week of suffering, which I see is a well documented rite of passage. (Although most evaluators will have left the building by the end of week one so it's not helping popularity. Perhaps there could be Squires of the Lambda Calculus who haven't done the vigil, mortification of flesh and so on?) Eventually a 3 page introduction on the O'Reilly website together with a good document called "Haskell for C Programmers" got me to the point where I could access "Yet Another Haskell Tutorial", and I was away... for a bit.
After a while I'd written some malformed horrors as answers to exercises, and was telling myself that it's probably just an edge effect - deep within a real Haskell program the ugliness would be invisible, tucked away in Ugly.hs. Then I discovered wxHaskell and got very excited. I really like wxWidgets, and I know it well. If I could play with some Haskell which manipulates wxWidgets I'd progress very quickly! I even have a recent C++ wxWidgets program which was annoying me as I wrote it because of the boilerplate. Great! I can play with type inference and mutter "about bloody time" with a smile on my face. Eventually I got a mix of wxWidgets 2.6.3, wxHaskell from darcs, ghc 6.6 running on my Mac, almost exactly as that permutation was described on the website, and I was off.
Within a few hours I had a nice frame, with 10 text controls and legends populating it. It already took less lines than C++, and then I discovered how to bundle all the text controls into a tuple to pass them around, and define some "getters" to access the tuple. My line count shrunk to what it "should" be - that is, something Kolmogorov wouldn't laugh at but is unattainable in C++. I had an onOpen which popped up a dialog and got a filename from the user, with all the properties filled in right. I proved that I could update the values in the text controls from the onOpen. Great. Next, translate the bit that says (pseudocode):
if(attempt_file_open) if(attempt_file_read) process
That's it. No fancy, complex error messages. Just check the error returns and only proceed if I have something to proceed with. Like grown-ups do. I *will* check my error returns. I have tormented too many newbies to *ever* consider doing anything else. If I cannot check my error returns I will not write the program. This is production programming 101, and I suspect that there's a real issue with priorities between the traditional Haskell community and production programmers.
So the time of madness began. I could find examples which did:
if(attempt_file_open) attempt_file_read process
Which is useless. Other examples did:
attempt_file_open if(attempt_file_read) process
Which is also useless. So I'm looking through the wxHaskell examples. They're all contrived, using very high level functions to (for example) read a complete image structure from a named file, which as one function had one possible source of errors. I go back to scanning and Googling like crazy. Eventually I notice a bit in "Yet Another Haskell Tutorial" - page 65 - which introduces "bracket", immediately explains that it is useless for my (very common) need, and refers the reader to Section 10 - "Advanced Techniques". The author of this excellent document hasn't yet written Section 10. I wonder why. I pause to examine bracket some more. I really can't tell if the "always" clause gets called if the initialization fails. From the use with file opening it looks like it isn't (or the handle to be closed would be invalid) but that wouldn't help if the initializer had two steps, e.g. called socket(2) and bind(2). This is the kind of thing good production programmers get really het up about.
I'm still grovelling through reams of stuff, trying to find out how to open *and* read a file in a grown up way, and I find various programs on haskell.org. There's an implementation of cat(1)! That's the thing! A file concatenator must be able to open *and* read it's file. Eagerly I download it. Curiously there doesn't seem to be any error handling. I compile it and point it at a non-existent file. The program crashes. Horribly. So much for Cat.hs. I feel glad I hadn't been able to cope with the video of Simon Peyton-Jones OSCON talk, because the camera operator kept filming his face while he's gesturing at the specific line of code he's talking about with a pointer! After seeing Cat.hs do essence of FAIL just opening a file, claims that Haskell could serve as a scripting language suitable for the crew of the Black Pearl in yonder corner to use would pain me.
Now I'm getting cross and turned off the whole business. I've been here before, grovelling through reams of stuff, trying to find something out while each example is contrived to side-step the problem, half-baked and useless, evasive or referencing non-existent documentation. All I need to make the experience complete is a certain firm's trademarks gratuitously embedded in the text at least once on every line. Then I'd be nauseous by now too.
Finally I found an uncommented example with no discussion called Hedi. This seems to be doing exception handling, but what it is doing, how, why, what can raise them, so on and so forth would presumably be covered in the "too complex to describe" bit of "Yet Another Haskell Tutorial". I tried to understand it from first principles, looking at the Type theology for the exceptions and various calls, but while I have the cup of tea I lack the necessary Total Perspective Vortex. I felt no confidence trying to even cut and paste it.
So question number one: Please - how do I do this?
Somehow I doubt that wrapping the complexity of opening a file *and* reading it in a grown up way and then documenting idioms that use the wrapper would help. If it would, the wrapper and doco would already exist. That implies that the complexity doesn't stack. If I get to the point where I can open a file *and* read it in a grown up way, and then I need to (say) verify a version number from the file:
if(attempt_file_open) if(attempt_file_read) if(version_ok) process
Would I need to completely restructure my program? I suspect so. But it's worth bringing it up. Question number two: Might wrapping the indescribable complexity of the most basic operations make it possible to publish and discuss some idioms which accomplish them? I'd be quite happy with a voodoo idiom for now. I know someone who (20 years on) still doesn't understand pointers to functions. She still uses qsort(3) by rote, and it works for her. That will do for beginners.
My biggest worry is that there's a really bad trade-off going on here. Sure, the structural instability of imperative parallel algorithms can be reduced, but at the cost of adding structural instability to everything else! TANSTAAFL and the lump in the carpet always goes somewhere. I read one chap who complained that in order to add one single line of debug he had to completely restructure his entire program, and then the line count was as big as an imperative language would be. Plus a world more complex I bet. It's this TANSTAAFL that's making me a non-believer.
So question number three (rhetorical): What would have happened if Codd and Date had tried to make SQL a general purpose programming language? Sequential file handling would have been impossible for all practical purposes, so no-one would have got the benefits of set theory (oh sorry relational calculus) and our most successful intentional language would have been stillborn. We'd still be using CODASYL.
Which leads to question number four: Why not do one job well? Limit parallelism to a single SMP machine where there are no comms to fail and failures which do occur will justify the OS chucking the whole process. Make an interface so that Haskell can be called from C++ or Java, with a simple IDL that can marshall nested structs, list<>s, map<>s and vector<>s in and out. Then we can get on with writing ambitious pure computations with names like sortOutRawCatScanData, tersely and in easily parallelizable ways, like we embed SQL to get certain specialist jobs done.
Then when all this was going on, question number five appeared: What the hell are these "lightweight Haskell threads"? Are they some kind of green threads running non-preemptively inside a single OS thread? Are they OS threads that could run concurrently on multi-core hardware? If they are green threads (and it sounds like they are) then this is all an academic exercise which has no production application yet.
I'm still hoping that this is solvable. That the instinctive priorities of production programmers are just different to those of researchers, and in fact it *is* possible to open a file *and* read it, checking *both* error returns, without being driven insane. If so, I sincerely suggest an example or two, like the small but well formed programs in K&R, Stroustrup or Gosling saying things like:
if((fp = fopen(...)) != NULL) { if(fgets(...) != NULL) { printf(...); }
fclose(...) }
Best wishes - and still hoping I'm wrong after all
Alan Carter
-- ... the PA system was moaning unctuously, like a lady hippopotamus reading A. E. Housman ..." -- James Blish, "They Shall Have Stars" _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- I try to take things like a crow; war and chaos don't always ruin a picnic, they just mean you have to be careful what you swallow. -- Jessica Edwards

Alan Carter wrote:
We now need to be able to do parallel with ease. Functional programming just got really important.
While this is a reason to have a look at Haskell, I think it's not the best one. In fact, I think it's probably harmful to have parallelism as single goal for learning Haskell: the language is very different from imperative languages and if you "just want to do parallelism" but otherwise stick to what you know, you'll have a really hard time.
Eventually a 3 page introduction on the O'Reilly website together with a good document called "Haskell for C Programmers" got me to the point where I could access "Yet Another Haskell Tutorial", and I was away... for a bit.
IMHO, the easiest way to learn Haskell and to appreciate functional programming is to learn it from a textbook :) The online tutorials are nice but you'll have a much harder time with them. I'd recommend Hutton's "Programming in Haskell" for the basics and Bird's "Introduction to Functional Programming using Haskell" for the functional style. See also http://haskell.org/haskellwiki/Books#Textbooks Of course, the textbooks (except "Real World Haskll" which is not done yet) most likely don't cover the System.IO stuff, but you've got the #haskell irc channel and the mailing list for that. So, the textbook remark is in anticipation of the questions that you are going to have :) (if you decide to pursue Haskell further, that is). Regards, apfelmus

Hi Alan I can help but feeling curious. Did some of the answers actually help you? Are you still as doubtful about Haskell as when you wrote your email? Greetings, Mads Lindstrøm
participants (20)
-
Alan Carter
-
Albert Y. C. Lai
-
Anton van Straaten
-
apfelmus
-
Bryan O'Sullivan
-
Cale Gibbard
-
Colin Paul Adams
-
Derek Elkins
-
Donn Cave
-
Jefferson Heard
-
jerzy.karczmarczuk@info.unicaen.fr
-
John Meacham
-
Jonathan Cast
-
Mads Lindstrøm
-
Philippa Cowderoy
-
Robert Dockins
-
Simon Marlow
-
Stefan O'Rear
-
Thomas Schilling
-
Wolfgang Jeltsch