
On 2/6/07, Yitzchak Gale
J. Garrett Morris wrote: Well, no, but it is at least no worse than
apply :: Handle -> Attribute a -> ContT (StateT Blargh (ErrorT Fzzt IO)) a
I find that in general, many functions do not need all of the capabilities. If they do, you can alias that also:
Well, in this case, the function looked more like: apply :: Handle -> Attribute a -> S a Part of the point here was that S was an abstraction. Most functions weren't accessing the state or the continuations directly - and their interaction with the error type had an intermediary as well. Instead, they were using operations that, in turn, used the underlying pieces.
You often need to make changes to the monad stack - add or remove capabilities, reorder, etc. This way, you only change the type in one place, and only fix functions that use the particular capabilities that were changed.
This is the same with my newtype-deriving alias.
The usual advantages of polymorphism apply - gives separation of concerns, encourages reuse, eases maintenance and testing, better expresses the meaning of the function by not mentioning unneeded details.
Well, we accomplished this all by having an abstraction barrier between a set of basic operations (which knew, at some level, about the internals of S and had their own sets of unit tests) and things built on top of S (which hypothetically could have gotten to its internals, but didn't. It would have been better practice to not export the instances, but I didn't think of that at the time_.
By the way, are you really doing CPS? If you are only using ContT to get short-circuiting, you could probably also simplify things by using ExitT instead:
We had a threading system which scheduled application threads to a limited number of IO threads based on data-driven changing priorities. This was first designed for GHC 6.2.2, when the threaded runtime wasn't in the shipping versions yet. I really think we're just talking about two approaches to the same thing. I prefer to encapsulate most of the MonadX operations as soon as possible behind a domain-specific layer, and then write the rest of my code in terms of that. In that case, I get isolation of concerns and testing and such from the fact that the internals of the monad stack aren't exposed, and if they need to be changed it only affects the DSL components, not the majority of the code. /g -- It is myself I have never met, whose face is pasted on the underside of my mind.