Here's where I'll heartily recommend the book The Haskell School of Expression: Learning Functional Programming through Multimedia
Well. As an interim measure I read the lecture slides. From that cursory glance--that actually went at about the right pace (4 or 5 slides representing a few 10s of pages)--it does at least appear to attempt to deal with (what I term) real-world problems.
However, and here's the reason I will want to browse the book before purchasing rather buying on line, it (appears) to skips the nasty little details.
For example, the treatment of the trivial, but strongly indicative example of the unix wc program. (Which I had to type in because it doesn't come as part of the source file distribution with the book)
import System
wcf :: ( Int, Int, Int ) -> String -> ( Int, Int, Int )
wcf ( cc, w, lc ) [] = ( cc, w, lc )
wcf ( cc, w, lc ) ( ' ' : xs ) = wcf( cc+1, w+1, lc )
+ xs
wcf ( cc, w, lc ) ( '\t' : xs ) = wcf( cc+1, w+1, lc )
+ xs
wcf ( cc, w, lc ) ( '\n' : xs ) = wcf( cc+1, w+1, lc+1 )
+ xs
wcf ( cc, w, lc ) ( x : xs ) = wcf( cc+1, w, lc )
+ xs
wc :: IO()
wc = do name <- getLine
contents <- readFile name
let ( cc, w, lc ) = wcf( 0, 0, 0 ) contents
putStrLn ( "The file:" ++ name ++ " has " )
putStrLn ( show cc ++ " chars " )
putStrLn ( show w ++ " words " )
putStrLn ( show lc ++ " lines." )
Compiling and running that fails because it has no main. Okay, it's off a slide, so add main = wc. Compile and I get a (500kb!) executable. Try running it--on a file with 2 lines of 30 spaces:
C:\ghc\test>wc 60sp2l.txt
wc: interrupted
C:\ghc\test>wc
60sp2l.txt
The file:60sp2l.txt has
62 chars
62 words
2 lines.
C:\ghc\test>wc
p:\test\1GB.dat
The file:p:\test\1GB.dat has
Heap exhausted;
Current maximum heap size is 268435456 bytes (256 Mb);
use `+RTS -M<size>' to increase it.
Impressed? Not! Doesn't handle command line parameters. No prompt. Counts spaces and newlines as words. Can't handle a big file.
Yes. I know it's only a demo. I should be able to add a prompt easily enough:
wcf :: ( Int, Int, Int ) -> String -> ( Int, Int, Int )
wcf ( cc, w, lc ) [] = ( cc, w, lc )
wcf ( cc, w, lc ) ( ' ' : xs ) = wcf( cc+1, w+1, lc ) x
+s
wcf ( cc, w, lc ) ( '\t' : xs ) = wcf( cc+1, w+1, lc ) x
+s
wcf ( cc, w, lc ) ( '\n' : xs ) = wcf( cc+1, w+1, lc+1 ) x
+s
wcf ( cc, w, lc ) ( x : xs ) = wcf( cc+1, w, lc
+) xs
wc :: IO()
wc = do putStr ( "Filename; " )
name <- getLine
contents <- readFile name
let ( cc, w, lc ) = wcf( 0, 0, 0 ) contents
putStrLn ( "The file:" ++ name ++ " has " )
putStrLn ( show cc ++ " chars " )
putStrLn ( show w ++ " words " )
putStrLn ( show lc ++ " lines." )
main = wc
Compile: C:\ghc\test>ghc -o wc.exe wc.hs
wc.hs:9:8: Parse error in pattern
Hmmm. Informative!
So, skip the prompt, and try and get the argument from the command line. Scan the library docs. System looks promising. But there is nothing that looks like it gives me access to the commmand line? Okay, skip that. Try dealing with the "words are spaces" problem. In an imperative language I'd simple 'remember' the previous character and treat consecutive spaces (and newlines) as a single delimiter for the purpose of counting words...but of course, that's state!
So, how about dealing with the memory issue? I thought the beauty of Haskell was that it was non-strict or lazy. That it dealt with infinite lists. Then why does getContents insist on loading the whole darn file?
Another example drawn from the same source.
From Chapter 8, containsS for Rectangles is defined as:
Rectangle s1 s2 `containsS` (x,y) =
let t1 = s1 / 2
t2 = s2 / 2
in -t1<=x && x<=t1 && -t2<=y && t2<=t2
But there is a classical GUI problem here. If you divide the screen into a grid, of say 10 x 10 pixels, then by the above definition any point on the right edge of one rectangle also appears on the left edge of the adjacent rectangle to its right. Likewise for points on the other three edges and their corresponding neighbours. This leaves a point at the crossroads between 4 adjacent squares testing as being contained within all four squares simultaneously! Might work for a Corner bet in roulette, but it surely doesn't work well for the majority of hit-testing purposes in graphical applications.
And that's the problem. All the demos are the same. They concentrate on (laborious formal) examination of Haskell's strengths and completely skip over all the messy edge cases.
All languages have their strengths and weaknesses. What defines a languages usability is the way in which it deals with it's weaknesses. What puts the P in Perl, is the practical way in which it has built-in mechanisms for dealing with the messiness of the real world--the edge cases--even where that means it has to eshew orthoganality and purity in order to achieve that practicality. Where the same problem in usability crops up frequently, the language has been extended and "special cased" to deal with that situation in a reasonable and usually quite intuative way. And the special cases are the subject of extensive documentation in the FAQ.
My problem with trying to get to grips with FP is that the FP language documentation reflects their theoretical origins by expounding on the formalism of their definitions extensively, but (from what I've seen so far) leaving the messy detail of dealing with the edge cases to .... I don't know yet. I haven't found the answer.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
|