Group: comp.lang.forth · Group Profile
Author: Jonah ThomasJonah Thomas Date: May 1, 2007 23:42
On Tue, 1 May 2007 23:01:04 +0200
mhx@iae.nl (Marcel Hendrix) wrote:
> Jonah Thomas cavtel.net> wrote Re: R> DROP in GForth
> [..]
>> So the new method adds essentially nothing to the complexity of the
>> system. It provides powerful flow control for slightly less overhead
>> than you'd use for errors.
>
> I remember from the old CLF BacForth threads that this leads to quite
> impenetrable, very difficult to read, code (mlg, Albert van der Horst,
> Chris Jakeman). And worse, I don't remember any useful examples of
> what could be done in much better ways.
>
> So maybe you could reverse the flow of arguments and first tell us
> what you want to accomplish with this proposal?
OK. I occasionally run into situations that seem to demand complex
control flow structures.
In theory any of these can be handled with some combination of IF ELSE
THEN and BEGIN UNTIL . These 5 words together can do anything, there's
no combination of cases that's impossible to do with combinations of
enough copies of these words. However, sometimes this results in code
that's very hard to write and very hard to read.
CASE statements were developed to handle some combinations of nested IF
statements. Sometimes jump tables work better.
WHILE REPEAT makes some loops easier.
DO LOOP and variants get the looping parameters off the stack and may
sometimes be efficient.
RECURSE simplifies some cases.
Sometimes it's an improvement to build finite state machines. Sometimes
they come out hard to debug.
My conclusion is that complex flow control can be hard to deal with, and
we have a large collection of special tricks that sometimes help with
that. But nothing works consistently or we'd use it consistently.
I have a vision of control flow with no loops that I don't want to do
ascii art for. The traditional image looks like two funnels put together
by their big ends. You get a cascade of IFs and ELSEs, at each step you
get twice as many. Then with each layer of THENs you get half as many
flows remaining until you reach a structured exit. Very pretty. And in
theory all it takes to represent any tree of nested IFs is to remove
whichever branches aren't needed.
But in practice you can do your nested IFs but when it comes time to do
the THENs it can go haywire. The code you finish up each section with
may not come out nice and smooth, you may need duplicate calls inside
various branches -- you may want to do routine A after several of the IF
branches but not their corresponding ELSEs etc. Sometimes a liberal use
of EXIT reduces the confusion.
One trick that helps a whole lot is careful naming. When you adapt the
control-flow structure to the user's view of the problem to be solved,
the complexities don't look that complex. You can follow it.
"International order or domestic? International orders require these
documents be registered with the federal government, and these further
documents for this list of nations. Then the shipping, calculate the
zones independent of borders but this list of states has a sales tax
proportional to the order, this list of nations has a fixed tax per
order, this other list of nations has both." Etc. When it's laid out
just like the domain expert wants it, he can usually see just what's
going on and correct errors easily. It may not be the most efficient way
to code it, but fitting his preconceptions is worth a lot.
Using something like CATCH THROW could provide a coherent way to arrange
things. For the combinatorial IFs you arrange the cases that work out
smoothly, and wherever it starts getting snarled you can do a LOB like
an EXIT except it sends your special LOB code to a particular place
instead of just going one level up. You can arrange whatever wrapping-up
you like, there. If a collection of unrelated branches require the same
finishing routine then give them all the same lob code and the calling
routine can do that routine for all of them. You can make a named
constant for that lob code that reminds you what needs to be done.
And with loops you can exit any time. No worries about doing UNLOOP the
right number of times, that's all taken care of. All you need is to make
sure the data you want to pass is on the data stack in the right order.
You can break out of BEGIN loops with precisely the right ending code by
using WHILE WHILE WHILE REPEAT ELSE THEN ELSE THEN etc. It can't be
factored and you have to match up the ELSEs and THENs just right.
Probably easier to use EXIT .
... IF FINISH-UP-RETURN-SHIPPING EXIT THEN ... IF
FINISH-UP-PROBLEMS-WITH-CUSTOMS EXIT THEN ... IF
FIHISH-UP-BILLING-ISSUES EXIT THEN ... UNTIL ...
This new method may be at least as good and it factors.
... IF RETURN-SHIPPING LOB THEN ... IF CUSTOMS-PROBLEMS LOB THEN ... IF
BILLING-ISSUE LOB THEN ... UNTIL ...
' HANDLE-ORDER CATCH
CASE
RETURN-SHIPPING OF ... ENDOF
CUSTOMS-PROBLEMS OF ... ENDOF
BILLING-ISSUE OF ... ENDOF
...
THROW
ENDCASE
You don't need one monolithic routine to LOB from, you can LOB from any
depth of nesting.
I haven't confirmed that this is useful to me. It seems plausible it
might be. Every now and then somebody facing some knotty problem
suggests "Hey, what about using CATCH THROW for this?" and somebody
reminds him that CATCH THROW is only for error conditions and it trashes
the data stack. Something that does a little less than CATCH THROW might
fit a collection of complex control-flow needs better than the tricks we
currently use.
|