IO monad and lazy evaluation

Glynn Clements glynn.clements@virgin.net
Wed, 21 May 2003 19:00:06 +0100


Graham Klyne wrote:

> I agree with the surface points you make.  It's easy enough to fix the 
> problem once you realize it's there.  (In my own "real" program, I moved 
> the hClose, which meant I had to pass the handle out of the function which 
> opened it.)
> 
> The underlying thrust of my post was that I thought pure functional 
> languages, like Haskell, were supposed to help one avoid such traps by 
> ensuring the messy dependencies on ordering didn't arise in the first 
> place.

Unfortunately, unless you also have a pure functional operating
system, the ordering of I/O operations matters ;)

  As things stand, I don't think I could begin articulate a reliable 
> set of rules for avoiding such problems, short of something like "use only 
> strict functions in monad-chains".  I'm hoping the Haskell community has 
> some experience with this kind of issue to offer some more helpful advice, 
> or even tools to detect unsafe combinations.  Maybe a discussion of safe 
> programming patterns would be a useful interim step?
> 
> (e.g. Ketil Z. Malde's suggestion of renaming the function to 
> hUnsafeGetContents maybe a small step in the right direction?)

Well, it is implemented using unsafeInterleaveIO; I'm not sure why
lazy I/O generally is considered unsafe but the specific cases of
readFile, hGetContents etc aren't.

> Thinking some more... I'm reminded of some discussions I had a few years 
> ago about the timing of calls to Java finalizers, and problems this could 
> cause for network I/O programs because using finalizers to close network 
> sockets would lead to unexpected resource problems.  The only reliable 
> solution was to always close the sockets explicitly when done.  With Java, 
> coming from C/C++, it was possible to get into a mindset that automatic 
> memory management also meant automatic management of all resources, 
> including all those that weren't directly visible to the programmer.  Maybe 
> there's a similar trap for the unware in Haskell?

More generally, the concept of "visible" semantics depends upon your
(highly subjective) definition of "visible". I'm reminded of a recent
BugTraq post regarding wiping sensitive information from memory; the
code was basically:

	char password[...];
	read_password(password);
	do_something(password);
	memset(password, 0, sizeof(password));
	return;

The compiler inlined the memset(), then noted that the contents of the
password array weren't used after the overwrite, so it optimised the
overwrite away.

-- 
Glynn Clements <glynn.clements@virgin.net>