
-----Original Message----- From: Manuel M. T. Chakravarty [SMTP:chak@cse.unsw.edu.au] Sent: Wednesday, May 09, 2001 12:57 AM To: brk@jenkon.com Cc: haskell-cafe@haskell.org Subject: RE: Functional programming in Python
[Bryn Keller] [snip]
and I have to agree with Dr. Mertz - I find Haskell much more palatable than Lisp or Scheme. Many (most?) Python programmers also have experience in more typeful languages (typically at least C, since that's how one writes Python extension modules) so perhaps that's not as surprising as it might seem.
Ok, but there are worlds between C's type system and Haskell's.[1]
[Bryn Keller] Absolutely! C's type system is not nearly so powerful or unobtrusive as Haskell's.
Type inference (to my mind at least) fits the Python mindset very well.
So, how about the following conjecture? Types essentially only articulate properties about a program that a good programmer would be aware of anyway and would strive to reinforce in a well-structured program. Such a programmer might not have many problems with a strongly typed language. [Bryn Keller] I would agree with this.
Now, to me, Python has this image of a well designed scripting language attracting the kind of programmer who strives for elegance and well-structured programs. Maybe that is a reason. [Bryn Keller] This, too. :-)
[Bryn Keller] [snip]
Absolutely. In fact, you have just pointed out one of the gripes that I have with most Haskell texts and courses. The shunning of I/O in textbooks is promoting the image of Haskell as a purely academic exercise. Something which is not necessary at all, I am teaching an introductory course with Haskell myself and did I/O in Week 5 out of 14 (these are students without any previous programming experience). Moreover, IIRC Paul Hudak's book http://haskell.org/soe/ also introduces I/O early.
In other words, I believe that this a problem with the presentation of Haskell and not with Haskell itself.
Cheers, Manuel
[1] You might wonder why I am pushing this point. It is just because the type system seems to be a hurdle for some people who try Haskell. I am curious to understand why it is a problem for some and not for others. [Bryn Keller] Since my first mesage and your and Simon Peyton-Jones' response, I've taken a little more time to work with Haskell, re-read Tackling the Awkward squad, and browsed the source for Simon Marlow's web server, and it's starting to feel more comfortable now. In the paper and in the server souce, there is certainly a fair amount of IO work happening, and it all looks fairly natural and intuitive.
Mostly I find when I try to write code following those examples (or so I think!), it turns out to be not so easy, and the real difficulty is that I can't even put my finger on why it's troublesome. I try many variations on a theme - some work, some fail, and often I can't see why. I should have kept all the versions of my program that failed for reasons I didn't understand, but unfortunately I didn't... The only concrete example of something that confuses me I can recall is the fact that this compiles: main = do allLines <- readLines; putStr $ unlines allLines where readLines = do eof <- isEOF if eof then return [] else do line <- getLine allLines <- readLines return (line : allLines) but this doesn't: main = do putStr $ unlines readLines where readLines = do eof <- isEOF if eof then return [] else do line <- getLine allLines <- readLines return (line : allLines) Evidently this is wrong, but my intuition is that <- simply binds a name to a value, and that: foo <- somefunc bar foo should be identical to: bar somefunc That was one difficulty. Another was trying to figure out what the $ sign was for. Finally I realized it was an alternative to parentheses, necessary due to the extremely high precedence of function application in Haskell. That high precedence is also disorienting, by the way. What's the rationale behind it? Struggling along, but starting to enjoy the aesthetics of Haskell, Bryn p.s. What data have your students' reactions given you about what is and is not difficult for beginners to grasp?

brk@jenkon.com wrote,
From: Manuel M. T. Chakravarty [SMTP:chak@cse.unsw.edu.au] Absolutely. In fact, you have just pointed out one of the gripes that I have with most Haskell texts and courses. The shunning of I/O in textbooks is promoting the image of Haskell as a purely academic exercise. Something which is not necessary at all, I am teaching an introductory course with Haskell myself and did I/O in Week 5 out of 14 (these are students without any previous programming experience). Moreover, IIRC Paul Hudak's book http://haskell.org/soe/ also introduces I/O early.
In other words, I believe that this a problem with the presentation of Haskell and not with Haskell itself.
Since my first mesage and your and Simon Peyton-Jones' response, I've taken a little more time to work with Haskell, re-read Tackling the Awkward squad, and browsed the source for Simon Marlow's web server, and it's starting to feel more comfortable now. In the paper and in the server souce, there is certainly a fair amount of IO work happening, and it all looks fairly natural and intuitive.
Mostly I find when I try to write code following those examples (or so I think!), it turns out to be not so easy, and the real difficulty is that I can't even put my finger on why it's troublesome. I try many variations on a theme - some work, some fail, and often I can't see why. I should have kept all the versions of my program that failed for reasons I didn't understand, but unfortunately I didn't... The only concrete example of something that confuses me I can recall is the fact that this compiles:
main = do allLines <- readLines; putStr $ unlines allLines where readLines = do eof <- isEOF if eof then return [] else do line <- getLine allLines <- readLines return (line : allLines)
but this doesn't:
main = do putStr $ unlines readLines where readLines = do eof <- isEOF if eof then return [] else do line <- getLine allLines <- readLines return (line : allLines)
Evidently this is wrong, but my intuition is that <- simply binds a name to a value, and that:
No, that is not the case. It does more, it executes an I/O action.
foo <- somefunc bar foo
should be identical to:
bar somefunc
But it isn't; however, we have do let foo = somefunc bar foo is identical to do bar somefunc So, this all boils down to the question, what is the difference between do let foo = somefunc -- Version 1 bar foo and do foo <- somefunc -- Version 2 bar foo The short answer is that Version 2 (the arrow) executes any side effects encoded in `somefunc', whereas Version 1 (the let binding) doesn't do that. Expressions given as an argument to a function behave as if they were let bound, ie, they don't execute any side effects. This explains why the identity that you stated above does not hold. So, at the core is that Haskell insists on distinguishing expressions that can have side effects from those that cannot. This distinction makes the language a little bit more complicated (eg, by enforcing us to distinguish between `=' and `<-'), but it also has the benefit that both a programmer and the compiler can immediately tell which expressions do have side effects and which don't. For example, this often makes it a lot easier to alter code written by somebody else. It also makes it easier to formally reason about code and it gives the compiler scope for rather radical optimisations. To reinforce the distinction, consider the following two pieces of code (where `readLines' is the routine you defined above): do let x = readLines y <- x z <- x return (y ++ z) and do x <- readLines let y = x let z = x return (y ++ z) How is the result (and I/O behaviour) different?
That was one difficulty. Another was trying to figure out what the $ sign was for. Finally I realized it was an alternative to parentheses, necessary due to the extremely high precedence of function application in Haskell. That high precedence is also disorienting, by the way. What's the rationale behind it?
You want to be able to write f 1 2 + g 3 4 instead of (f 1 2) + (g 3 4)
p.s. What data have your students' reactions given you about what is and is not difficult for beginners to grasp?
They found it to be a difficult topic, but they found "Unix/Shell scripts" even harder (and we did only simple shell scripts). I actually made another interesting observation (and keep in mind that for many that was their first contact with programming). I had prepared for the distinction between side effecting and non-side-effecting expressions to be a hurdle in understanding I/O. What I hand't taken into account was that the fact that they had only worked in an interactive interpreter environment (as opposed to, possibly compiled, standalone code) would pose them a problem. The interactive interpreter had allowed them to type in input and get results printed all way long, so they didn't see why it should be necessary to complicate a program with print statements. I append the full break down of the student answers. Cheers, Manuel -=- Very difficult Average Very easy Recursive functions 3.8% 16.1% 44.2% 25.2% 12.1% List processing 5.2% 18% 44% 25.4% 8.8% Pattern matching 3% 15.2% 41.4% 27.8% 14% Association lists 4.5% 28.5% 48.5% 15.4% 4.5% Polymorphism/overloading 10.9% 44.2% 37.8% 5.9% 2.6% Sorting 5.7% 33.5% 47.6% 11.6% 3% Higher-order functions 16.9% 43% 31.4% 8.5% 1.6% Input/output 32.6% 39.7% 19.7% 7.3% 2.1% Modules/decomposition 12.8% 37.1% 35.9% 12.1% 3.5% Trees 29.5% 41.9% 21.9% 5.7% 2.6% ADTs 35.9% 36.4% 20.9% 6.1% 2.1% Unix/shell scripts 38.5% 34.7% 20.7% 5.7% 1.9% Formal reasoning 11.1% 22.6% 31.9% 20.9% 15%
participants (2)
-
brk@jenkon.com
-
Manuel M. T. Chakravarty