
Chris Smith wrote:
I'm primarily interested in the two cases where one simply has no choice about the use of monads: and those are IO and STM. No, this is not purely functional programming then; but it has some very compelling advantages to Haskell's implementation of these, that I'm afraid are currently quite hidden behind the difficult syntax. Using something besides a monad is simply not an option.
"The more tiresome monads are, the more incentive you have to avoid them." Unfortunately, I'm afraid this cheapens work people are doing in making the necessary imperative parts of Haskell more useful and interesting. Making monads distasteful is not a reasonable goal.
Can you think of a single widely used programming language that forces programmers to write linear one-line-per-operation code like this? IMO, Haskell gets away with this because STM and IO stuff isn't very common; and STM and IO will remain uncommon (and will instead be replaced by unsafe code written in Python or Ruby) as long as this is the case.
I mean it in the following way: the power of Haskell is that a large core of pure functions does the actual algorithmic work and is surrounded by a small layer of imperative code. It's not possible to avoid the small layer of imperative code, of course. But the more you treat imperative code as somewhat pure, the greater the danger that the purely functional logic will be buried inside a mess of imperative code. In other words, the goal is exactly to make IO and STM uncommon, otherwise you loose the power the purely functional approach offers. What I want to say is: if you feel the urge to use the monad splicing syntax, then I think that there's a big chance that the code you write is in essence pure and can also be made completely pure. That's why I'd like to see the code that made you give up. It may require much more pondering to find a pure abstraction to the programming problem at hand. But once found, it bests any ad-hoc code. For example, take the HGL (Haskell Graphics Library) which actually shows the boundary between pure and monad. The main abstraction is the type Graphic that represents a graphic to be drawn on the screen. It's implemented with a monad Draw a with in turns does IO to draw itself on the screen. But the abstraction is to treat this as a purely functional value with operations like emptyGraphic :: Graphic polygon :: [Point] -> Graphic overGraphic :: Graphic -> Graphic -> Graphic to create and compose graphics. To draw a graphic, you have to use IO. But his is no reason not to offer a pure abstraction even if the internals are littered with IO. HGL still exports the Draw monad type Graphic = Draw () and I consider this a sin. It only appears as monad in the three functions selectBrush :: Brush -> Draw Brush selectPen :: Pen -> Draw Pen selectFont :: Font -> Draw Font which exist to enable the user to hand-optimize a bit since brush, font and pen creation is expensive on Win32. But arguably, these optimizations can be performed automatically under the hood. An interesting example of how a purely functional data structure makes life much easier is described in N. Ramsey and J. Dias. An Applicative Control-Flow Graph Based on Huet's Zipper http://www.eecs.harvard.edu/~nr/pubs/zipcfg-abstract.html <abstract>We are using ML to build a compiler that does low-level optimization. To support optimizations in classic imperative style, we built a control-flow graph using mutable pointers and other mutable state in the nodes. This decision proved unfortunate: the mutable flow graph was big and complex, and it led to many bugs. We have replaced it by a smaller, simpler, applicative flow graph based on Huet's (1997) zipper. The new flow graph is a success; this paper presents its design and shows how it leads to a gratifyingly simple implementation of the dataflow framework developed by Lerner, Grove, and Chambers (2002).</abstract> That being said, it is of course desirable to be able to describe genuinely imperative behavior like resource (de-)allocation elegantly in Haskell. Not everything can be pure :) (or rather :( ). But I'm not sure whether the linearization imposed is really an issue then.
Ultimately, it may just have to come down to implementing the extension, making it available as an extension to GHC and perhaps other Haskell compilers.
As it stands, though, I'm just not sure how to evaluate ideas without language changes against an alternative that doesn't exist.
Hm, it seems slightly unfair to me to leave the burden of searching for an alternative to somebody else.
I can explain desugaring rules for this idea in a short paragraph. The alternatives all seem to involve operators and functions that I've not used in about six months or more of moderate playing around with Haskell.
In fact, applicative functors are a very useful and powerful abstraction and to some extend, they exactly solve the problem of programming with monads in an applicative style. I would be sad if you'd ignore them in case they solve your STM-code problem without compiler extension. Regards, apfelmus