
I'm not sure this example really has anything to do with state. Is there
anything about your domain & examples that differs from standard functional
programming (or math)? To my eye, your example code below looks less like
an imperative program than like an intermediate form that a compiler would
generate from an expression built up from nested function applications and a
few "let"s.
Cheers, - Conal
On 12/24/06, Steve Schafer
In my text/graphics formatting work, I find myself doing a lot of "pipeline" processing, where a data structure will undergo a number of step-by-step transformations from input to output. For example, I have a function that looks like this (the names have been changed to protect the innocent--and to focus on the structure):
process :: a -> b -> c -> d -> e process x1 x2 x3 x4 = let y01 = f01 x1 x2 x3; y02 = f02 x1; y03 = f03 y02; y04 = f04 y03; y05 = f05 x1 y01 y04; y06 = f06 x2 y05; (y07,y08) = f07 y01 y06; y09 = f08 y07; (y10,y11) = f09 x2 x4 y09 y08; y12 = f10 y10; y13 = f11 y12; y14 = f12 x1 x2 x3 y01 y13; y15 = f13 y14; y16 = f14 y15 y11 in y16
As you can see, the process is somewhat imperative in overall appearance, with each intermediate function f01..f14 accepting some combination of the original input values and/or intermediate values and returning a new value (or, in some cases, a tuple of values).
Obviously, not all of the steps need to be broken out this way. We can, for example, skip the second and third steps and directly write:
y04 = f04 $ f03 $ f02 x1;
Laying it out this way has a couple of advantages. It makes the steps in the process transparently clear, and if I discover at some point that I need to make a change (e.g., a new requirement causes f13 to need access to x2), it's perfectly obvious where to make the modifications.
Nevertheless, it also looks like something that would be amenable to processing with a State monad, except for one thing: The "shape" of the state isn't constant throughout the process. At any given step, new information may be added to the state, and old information may be thrown away, if there is no further need for it. In principle, it could be managed with a bunch of nested StateT monads, but my attempts to do so seem to get so caught up in the bookkeeping that I lose the advantages mentioned above.
Alternatively, I can wrap all of the state up into a single universal structure that holds everything I will ever need at every step, but doing so seems to me to fly in the face of strong typing; at the early stages of processing, the structure will have "holes" in it that don't contain useful values and shouldn't be accessed.
So here's the question: Is there a reasonable way to express this kind of process (where I suppose that "reasonable" means "in keeping with Haskell-nature") that preserves the advantages mentioned above, but avoids having to explicitly pass all of the various bits of state around? The (unattainable?) ideal would be something that looks like this:
process = f14 . f13 . ... . f01
or
process = f01 >>= f02 >>= ... >>= f14
Steve Schafer Fenestra Technologies Corp. http://www.fenestra.com/ _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe