[Haskell-cafe] Re: Total Functional Programming in Haskell

wren ng thornton wren at freegeek.org
Wed Oct 1 19:53:18 EDT 2008

Jason Dagit wrote:
> I was asserting that Haskell is currently 2 layered.  Purely functional vs.
> IO.  They integrate nicely and play well together, but I still think of them
> as distinct layers.  Perhaps this is not fair or confusing though.  The
> paper I cited did indeed use codata to define streams so that something,
> such as an OS, that needs to process infinite streams of requests can still
> do so.

For a first take on monads, that's not a bad way of thinking about it. 
But, once you move beyond first thoughts, monads aren't a special 
separate layer and thinking of them as such is liable to land you in 
trouble. Monads really are pure, it's just that they get there by 
existentializing over impurities.[1]

Perhaps you really do mean only the IO monad and not any others, but 
then the question becomes: whose IO? The IO monad is well-known to be a 
sin bin for things people don't know or care enough about. Over time 
things get added to IO and over time things get broken off into their 
own monads. So then where is the layer boundary over time? I assert that 
there's no meaningful place to draw the boundary because there's nothing 
particularly special about IO that isn't shared by other monads like ST 
or ACIO.

It'd be nice to have a total core language, but it's not clear how to 
helpfully represent such things in the type system. One variety of 
non-totality is the |error| function. We could design our types to say 
that people can't call it, but the whole purpose of |error| is to raise 
exceptions (a monadic action) from pure code; so as far as the types are 
concerned, we already can't do any such things. We can eliminate many 
instances of those non-totalities by adding in refinement types (a 
modest proposal), in order to prevent people from passing [] to |head| 
or |tail|, or passing 0 as the denominator of (/), etc.

But there are other non-totalities, such as _|_ which cannot be captured 
by such means. In order to capture many instances of _|_ we'd need to 
have our type represent their depth so that we can do co/induction in 
order to ensure that the function terminates. While we can capture a 
surprising level of such detail in Haskell's type system, this is 
marching off in the direction of dependent types which would be making 
things more complex rather than simpler. I'm not saying that we 
shouldn't head there, but it doesn't seem to be addressing the goals you 
had in mind.

[1] Just like existential types, you can put something in but you can 
never get it back out again. For inescapable monads like IO and ST, this 
is why they have the behavior of sucking your whole program into the 
existential black-hole.

Live well,

More information about the Haskell-Cafe mailing list