I have a general comment about this thread. It seems to me to point
up the difficulty of writing first-class control structures in REBOL.
I haven't seen in this thread (nor have I been able to think of) an
approach that actually behaves as well as a native control structure.
To illustrate this, let me take two of the best examples from the
thread and show how to break (or, at least, expose) them.
[EMAIL PROTECTED] wrote:
>
> pif: func [[throw]
> {polymorphic if with lazy evaluation and minimal checking}
> args [block!] /local cond blk r result
> ] [
> result: false
> while [not empty? args] [
> either cond: first r: do/next args [
> either logic? cond [
> r: do/next second r
> if not unset? blk: first r [
> result: do blk
> ]
> ] [
> result: do cond
> ]
> args: tail args
> ] [
> r: do/next second r
> args: second r
> ]
> ]
> result
> ]
When I use this proposal as follows
testcase2: func [/local a b] [
for a 1 3 1 [
for b 1 3 1 [
pif [
a < b [print [a "<" b]]
a = b [print [a "=" b]]
a > b [print [a ">" b]]
]
]
]
]
I get the following:
>> testcase2
1 = 1
** Script Error: result needs a value.
** Where: result: do blk
due to the fact that print doesn't return a result:
>> foo: print 123
123
** Script Error: foo needs a value.
** Where: foo: print 123
Therefore, caching the result via result: do blk can break if
there's no result from do blk .
I did think about wrapping all of that in a try block, and then
playing tricky flag games at the end of the function based on
whether result ever got a value, but that seemed unbearably
crufty.
It also didn't solve the whole problem, AFAICT, due to the same
issue that occurred in this proposal:
[EMAIL PROTECTED] wrote:
>
> cases-dialect: make object! [
> "Dialect for do-cases"
> else-if: if: func [
> condition
> body [block!]
> ] [
> system/words/if condition [
> do body
> true
> ]
> ]
> else: :do
> ]
>
> do-cases: func [
> "Executes the case whose condition is true"
> ; example for cases:
> ; if cond1 [code1] else-if cond2 [code2] ... else [default]
> cases [block!]
> ] [
> any bind cases in cases-dialect 'self
> ]
>
Again, a nice piece of code, but look what happens with
paranoid1: func [a b] [
do-cases [
a <= 0 [return 0]
b <= 0 [return 0]
else [print "OK"]
]
print "Computing now!"
]
which is obviously a stripped-down skeleton of a function that
wants to be sure it was given safe arguments.
>> paranoid1 4 6
Computing now!
>> paranoid1 -4 6
Computing now!
>> paranoid1 4 -6
Computing now!
>> paranoid1 -4 -6
Computing now!
The problem, AFAICT, is that the [return 0] is being evaluated in
do-cases, which returns a zero result to paranoid1 (as the value of
the do-cases evaluation), which then happily falls through to
the evaluation of the rest of its body.
SO...
To build a truly first-class control structure, we must pass to
it one or more blocks which will be conditionally/repetitively
evaluated WITHIN THE CONTROL STRUCTURE ITSELF. How can we make
the "natural" behavior occur within the caller for all of
controlled block that evaluates to a value
controlled block that evaluates with no value (just a
side effect, such as 'print)
controlled block that evaluates 'return (producing
a return from the CALLER)
(are there any others?)
The closest I've been able to come to addressing these behavioral
issues is
first-true: func [b [block!]] [
foreach [c r] b [
if do c [return r]
]
return []
]
which would have to be used as follows:
testcase3: func [/local a b] [
for a 1 3 1 [
for b 1 3 1 [
do first-true [
(a < b) [print [a "<" b]]
(a = b) [print [a "=" b]]
(a > b) [print [a ">" b]]
]
]
]
]
paranoid3: func [a b] [
do first-true [
(a <= 0) [return 0]
(b <= 0) [return 0]
(true ) [print "OK"]
]
print "Computing now!"
]
with results of
>> paranoid3 4 6
OK
Computing now!
>> paranoid3 -4 6
== 0
>> paranoid3 4 -6
== 0
>> paranoid3 -4 -6
== 0
>> testcase3
1 = 1
1 < 2
1 < 3
2 > 1
2 = 2
2 < 3
3 > 1
3 > 2
3 = 3
Well... This addresses the behavioral problems, but at a TERRIBLE
cost in appearance! Having to have the explicit 'do in the caller
certainly breaks any illusion that this is a "real" control
structure. (I am assuming that a do/next approach would take
care of the need for parens on the condition. I just wanted to get
the 'do issue out for discussion with minimal coding.)
Any more thoughts, anyone?
-jn-