[Haskell-cafe] Haskell & monads for newbies
Jonathan Cast
jcast at ou.edu
Sun Jul 15 15:28:56 EDT 2007
On Sunday 15 July 2007, Paul Moore wrote:
> On 15/07/07, Andrew Coppin <andrewcoppin at btinternet.com> wrote:
> > I guess because in most normal programming languages you can do I/O
> > anywhere you damn like, it doesn't occur to most programmers that it's
> > possible to make a seperation. (Most seem to realise that, e.g., mixing
> > business logic with GUI code is a Bad Thing though...)
>
> Hmm, I would speculate (I have no hard data, in other words...) that
> it's more the case that in imperative languages, you do I/O throughout
> the program, because that defers the I/O (which is slow) to the last
> possible moment, and it allows you to reuse memory buffers.
>
> People's intuition about performance and memory usage says that
> delaying I/O is good, and "separating" I/O and logic (which is taken
> to mean slurping data in all at once, and then processing it) is
> memory intensive and risks doing unnecessary I/O.
>
> Haskell handles this with laziness. The canonical example is counting
> characters in a file, where you just grab the whole file, and use
> length. An imperative programmer's intuition says that this wastes
> huge amounts of memory compared to reading character by character and
> incrementing a count. Lazy I/O means that no more than 1 character
> needs to be in RAM at any one time, without the programmer need to do
> the bookkeeping.
>
> If lazy I/O was publicised in this way, as separation of concerns (I/O
> and processing) with the compiler and language handling the work of
> minimising memory use and avoiding unnecessary I/O, then maybe the
> message might get through better. However, the only article I've ever
> seen taking this approach (http://blogs.nubgames.com/code/?p=22)
> didn't seem to get a good reception in the Haskell community, sparking
> comments that hGetContents and similar functions had a number of
> issues which made them "bad practice". The result was to leave me with
> a feeling that separating I/O and processing in Haskell really was
> hard, but I never quite understood why...
Because hGetContents only buys you laziness /if you use it lazily/. And
laziness is, technically, a denotational property, but it is a very
operational-feeling denotational property. And operational reasoning is
difficult in imperative languages and gets really, really hard in lazy
functional languages. And the article you cite falls flat on its face in
trying to be lazy:
> readWithIncludes :: String -> IO [String]
> readWithIncludes f = do
> s <- readFile f
> ss <- mapM expandIncludes (lines s)
> return (concat ss)
> expandIncludes :: String -> IO [String]
> expandIncludes s =
> if isInclude s
> then
> readWithIncludes (includeFile s)
> else
> return [s]
That's calling mapM, a strict function, on the result of lines ss --- an
arbitrarily long list.
More generally, I suspect the Haskell community has a collective memory of
stream I/O, back when this sort of thing used to be /really, really
important/, because your program had type [Response] -> [Request] and if it
wasn't lazy enough in its argument, you'd get a deadlock --- and that
deadlock had nothing whatsoever to do with the result of applying your
function to total arguments, so reasoning about it required abandoning every
Haskeller's instinct to reason about functions only over total (or even
finite total) arguments. interact takes a function with a type eerily
similar to [Response] -> [Request], which means its argument has all the same
problems. Laziness is great and everything --- but it's a lot of work, even
in Haskell.
> So I guess that leaves me with the question: is separating I/O and
> processing really the right thing to do (in terms of memory usage and
> performance) in Haskell, and if so, why isn't it advertised more? (And
> for extra credit, please explain why the article I quoted above didn't
> make more of an impact in the Haskell community... :-))
Jonathan Cast
http://sourceforge.net/projects/fid-core
http://sourceforge.net/projects/fid-emacs
More information about the Haskell-Cafe
mailing list