Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

More on Boolean Shortcircuiting

111 views
Skip to first unread message

Arnold Doray

unread,
May 26, 2012, 5:39:12 AM5/26/12
to
Dear Forthers,

I have been giving boolean short-circuiting more thought, and would
appreciate your comments.

Consider the example of having some image handles on the data stack. The
task is to assign these to one of two variables ( FIRST and SECOND ),
given some criteria. FIRST must be tried first, then SECOND. Rejected
image handles are dropped. The number of images is given in COUNT.

A first pass solution:

variable first
variable second

\ Aborts the processing if there are too few images.
: too-few-images? ( -- )
count @ 2 < "Too few images." log-and-exit! ;

\ Criteria for image handles
: nil? ( tgt -- f )
: newer? ( src tgt -- f ) ... ;
: higher-priority? ( src tgt -- f ) ... ;

\ Runs the criteria checks for an image against a variable.
: replace? ( src tgt -- src tgt f )
dup nil? if true exit then
2dup newer? if true exit then
2dup higher-priority? if true exit then
false ;

\ Assigns image handles on the data stack.
: process ( image* -- )
too-few-images?
count @ 0 do
first @ replace?
if first !
else second @ replace?
if second !
else drop then
then
loop;

This does the job, but:

1) REPLACE? could be tidied up with boolean short-circuiting,
2) The nesting of PROCESS can quickly become a mess if later more
variables are added later (THIRD, FOURTH, etc).
3) The code is not very clear, even for this simple example.
4) Using an exception to abort processing means it must be caught
somewhere by PROCESS's caller.

One obvious factoring is to use a short-circuiting operator:

: | ` if ` true ` exit ` then ; immediate

With which REPLACE? becomes:

: replace? ( src tgt -- src tgt f )
dup nil? | 2dup newer? | 2dup higher-priority? | false ;

Which is more readable. We can also then dispense with using exceptions
in TOO-FEW-IMAGES?

: too-few-images? ( -- f )
count @ 2 < dup if "Too few images to process." log then ;

: process ( image* -- f )
too-few-images? | count @ 0 do ... false ;

And the nested IFs in PROCESS can be factored too:

: assign? ( img xt -- img F | T )
tuck @ replace? nip if swap ! true else swap drop false then ;

: assign ( img -- f )
first assign? | second assign? | drop false ;

: assign-all ( images -- f )
count @ 0 do assign drop loop false ;

\ False indicates success.
: process ( images* -- f )
too-few-images? | assign-all ;

Which is more readable. But ASSIGN? is a mess and the return value of
PROCESS is not intuitive. Both could be improved using another short-
circuiting operator:

: -> ` not ` if ` false ` exit ` then ; immediate

: assign? ( img xt -- img f )
@ replace? nip ;

: assign-first? ( src -- src F | T )
first assign? -> first ! true ;

: assign-second? ( src -- src F | T )
second assign? -> second ! true ;

: assign ( src -- f )
assign-first? | assign-second? | drop false ;

: assign-all ( images -- f )
count @ 0 do assign drop loop true ;

\ Returns true on success.
: process ( images* -- f )
too-few-images? not -> assign-all ;

This code looks clearer to me, and extensions to include more variables/
criteria is obvious. Also, there is no need to catch exceptions.

Do you think the short-circuiting operators:

: | ` if ` true ` exit ` then ; immediate
: -> ` not ` if ` false ` exit ` then ; immediate

have general utility outside this simple example? Can they be improved?

Thanks,
Arnold

Josh Grams

unread,
May 26, 2012, 8:44:12 AM5/26/12
to
Arnold Doray wrote: <jpq8c0$30h$1...@dont-email.me>
> Dear Forthers,
>
> I have been giving boolean short-circuiting more thought, and would
> appreciate your comments.

> A first pass solution:

<SNIP>

> This does the job, but:
>
> 1) REPLACE? could be tidied up with boolean short-circuiting,

Yes, although it's readable, idiomatic Forth as-is.

> 2) The nesting of PROCESS can quickly become a mess if later more
> variables are added later (THIRD, FOURTH, etc).

Yikes. Don't do that. When coding there are only three quantities;
zero, one, and many. :)

For more than two slots, use an array and an inner loop:

: assign ( x addr u -- )
cells over + swap ?do
dup i @ replace? if dup i ! leave then
1 cells +loop drop ;

> 3) The code is not very clear, even for this simple example.

Looks pretty clear to me...

> 4) Using an exception to abort processing means it must be caught
> somewhere by PROCESS's caller.

Then don't abort from `too-few-images?`. Just do the check there. Log
and exit from `process`.

: process ( i*x -- )
too-few-images? if s" Too few images to process." log else
...
then ;

Or maybe log the error in `too-few-images?` but exit from `process`,
either way.

>: -> ` not ` if ` false ` exit ` then ; immediate
>
>: assign? ( img xt -- img f )
> @ replace? nip ;
>
>: assign-first? ( src -- src F | T )
> first assign? -> first ! true ;
>
>: assign-second? ( src -- src F | T )
> second assign? -> second ! true ;
>
>: assign ( src -- f )
> assign-first? | assign-second? | drop false ;

> Do you think the short-circuiting operators:
>
>: | ` if ` true ` exit ` then ; immediate
>: -> ` not ` if ` false ` exit ` then ; immediate

I think the second is usually called `&` rather than '->'.

> have general utility outside this simple example? Can they be improved?

Some people like them, some don't. There are also versions which don't
return a flag, and finding clear short names for all four is a bit
tricky.

One disadvantage, as in your code above, is having to have a separate
word for each branch -- this often forces you to name things that
shouldn't really need to be.

Using them like this is fairly harmless, I'd say. But I don't usually
bother; I just write them out. Putting one per line is fairly readable.

Some people go even farther and manipulate the rstack to return from
multiple words -- Michael Gassanenko (sp?) has written a bunch about
this. I find that leads to brittle code, but that may just be me.

--Josh

Josh Grams

unread,
May 26, 2012, 8:47:21 AM5/26/12
to
Josh Grams wrote: <4fc0d01b$0$23934$882e...@usenet-news.net>
> Arnold Doray wrote: <jpq8c0$30h$1...@dont-email.me>
>> 2) The nesting of PROCESS can quickly become a mess if later more
>> variables are added later (THIRD, FOURTH, etc).
>
> Yikes. Don't do that. When coding there are only three quantities;
> zero, one, and many. :)
>
> For more than two slots, use an array and an inner loop:

Hmm. Also I think your algorithm is wrong. You almost certainly want
to shift the items down rather than overwriting the most important one
left...

--Josh

Arnold Doray

unread,
May 26, 2012, 9:48:06 AM5/26/12
to
On Sat, 26 May 2012 12:44:12 +0000, Josh Grams wrote:

<snip>

Thank you for your comments. BTW, the algorithm is incorrect, but I meant
it just as a simple illustration.

>
> Some people go even farther and manipulate the rstack to return from
> multiple words -- Michael Gassanenko (sp?) has written a bunch about
> this. I find that leads to brittle code, but that may just be me.

Samuel Falvo has described a similar style of programming he calls
"Declarative" programming in Forth. I've not read any of Gassaneko's
work, but Falvo also uses rstack manupilation. This isn't always possible
because not all forths use the Rstack for storing return addresses. The
portable way, as described by Andrew Haley, is to throw exceptions:

: exit! if true throw then ;

I find this brittle too, because it's hard to tell where the exception
comes from. I developed (or more precisely, re-invented) this other sytle
using | and -> as an alternative.

Cheers,
Arnold

Arnold Doray

unread,
May 26, 2012, 10:16:15 AM5/26/12
to
On Sat, 26 May 2012 12:47:21 +0000, Josh Grams wrote:

> Hmm. Also I think your algorithm is wrong. You almost certainly want
> to shift the items down rather than overwriting the most important one
> left...

With corrected algorithm:

: | ` if ` true ` exit ` then ; immediate
: -> ` not ` if ` false ` exit ` then ; immediate

variable first
variable second

: too-few-images? ( -- f )
count @ 2 < dup if "Too few images to process." +log then ;

: nil? ( src -- f ) ... ;
: newer? ( tgt src -- f ) ... ;
: higher-priority? ( tgt src -- f ) ... ;

: replace? ( tgt src -- tgt src f )
dup nil? | 2dup newer? | 2dup higher-priority? | false ;

: assign? ( img xt -- img f )
@ replace? nip ;

: demote ( xt-tgt xt-src -- )
@ swap ! ;

: assign-first? ( img -- img F | T )
first assign? -> second first demote
first ! true ;

: assign-second? ( img -- img F | T )
second assign? -> second ! true ;

: assign ( img -- f )
assign-first? | assign-second? | drop false ;

: assign-all ( -- )
count @ 0 do R> assign drop loop true ;

\ Returns true on success.
: process ( -- f )
too-few-images? not -> assign-all ;

BruceMcF

unread,
May 26, 2012, 11:27:27 AM5/26/12
to
On May 26, 5:39 am, Arnold Doray <inva...@invalid.com> wrote:

> I have been giving boolean short-circuiting more thought, and would
> appreciate your comments.

> Consider the example of having some image handles on the data stack. The task is to assign these to one of two variables ( FIRST and SECOND ), given some criteria. FIRST must be tried first, then SECOND. Rejected image handles are dropped. The number of images is given in COUNT.

So, if that means to assign the best two of a set of img's on the
stack, the best :

\ null-image ( -- img ) gives a null image reference

: assign-img? ( img addr -- flag )
DUP >R @ ( img1 img2 )
replace? ( img1 img2 flag ) IF
DROP R> !
ELSE TWODROP RDROP FALSE
THEN

: best-two-images ( img1 ... imgn -- )
null-image FIRST ! null-image SECOND !
BEGIN
COUNT @ WHILE
-1 COUNT +!
DUP FIRST @ FIRST assign-img? IF
DROP
ELSE SECOND @ SECOND assign-img? DROP
THEN
REPEAT ;

The consumer of best-two-images just tests FIRST or SECOND for null-
images to see if it worked, so "not enough images" error passing is
not needed.

Factoring that with your words, I'll call them |else and |if because I
don't want to use up such great application specific word names as |
and -> for something that would go into a file with a set of general
utility words.

: -- ( addr -- ) -1 SWAP +! ;
\ G* COMUS for decrement a variable by 1

: init-images ( -- ) null-image DUP FIRST ! SECOND ! ;

: replace? ( img1 img2 -- img1 img2 flag )

: replace? ( src tgt -- src tgt f )
DUP nil? |else 2DUP newer? |else 2DUP higher-priority?
|else FALSE ;

: replaced-img? ( img1 addr -- flag )
TUCK @ replace? NIP DUP >R IF SWAP ! ELSE 2DROP THEN R> ;

: better-images ( img1 -- )
\ G* put in FIRST if it improves it
\ or else in SECOND if it improves that
DUP FIRST replaced-img? NOT IF SECOND replaced-img? THEN DROP ;

: best-images ( img1 ... imgn -- )
init-images BEGIN COUNT @ WHILE better-images COUNT -- REPEAT ;

Yeah, I think that the |else fits.

BruceMcF

unread,
May 26, 2012, 1:25:57 PM5/26/12
to
Oops, was thinking of doing something but got distracted ... there's a
flaw in that logic, since it doesn't ensure that FIRST is best.


> \ null-image ( -- img ) gives a null image reference

> The consumer of best-two-images just tests FIRST or SECOND for null-
> images to see if it worked, so "not enough images" error passing is
> not needed.

A result of how well it worked could be returned ~ TRUE both filled, 1
FIRST filled, FALSE neither filled.

> Factoring that with your words, I'll call them |else and |if because I
> don't want to use up such great application specific word names as |
> and -> for something that would go into a file with a set of general
> utility words.
>
> : -- ( addr -- ) -1 SWAP +! ;
>    \ G* COMUS for decrement a variable by 1
>
> : init-images ( -- ) null-image DUP FIRST ! SECOND ! ;
>
> : replace? ( img1 img2 -- img1 img2 flag )
>
> : replace? ( src tgt -- src tgt f )
>    DUP nil? |else 2DUP newer? |else 2DUP higher-priority?
>    |else FALSE ;

Now, I return the img not placed into the variable, so that when used
on FIRST first, then what is in FIRST is the superior of the two if
either are, and what is on the stack is the inferior of the two if
either are ... then that will *always* filter through to SECOND. Since
we always filter through, the flag is not needed:

: replaced-img ( img1 addr -- img2 )
   TUCK @ replace? IF SWAP ROT ! ELSE 2DROP THEN ;

: better-images ( img1 -- ) FIRST replaced-img SECOND replaced-img
DROP ;
   \ G* put in FIRST if it improves it
   \    or else in SECOND if it improves that

The first non-null will go into FIRST, the second non-null will go
into FIRST if its better than the first, SECOND if its worse, and then
each use of better-images will keep them in order.

Now, to add the result return:

: ?1+ ( u1 flag -- u2 ) IF 1- THEN ;
\ G* u2=u1+1 on TRUE

: result-images ( -- u ) 0 FIRST @ null? NOT ?1+ SECOND @ null? NOT ?
1+ ;

: best-images ( img1 ... imgn -- u )
init-images BEGIN COUNT @ WHILE better-images COUNT -- REPEAT
result-images ;

David N. Williams

unread,
May 26, 2012, 5:17:30 PM5/26/12
to
On 5/26/12 5:39 AM, Arnold Doray wrote:
> Dear Forthers,
>
> I have been giving boolean short-circuiting more thought, and would
> appreciate your comments.
>
> Consider the example of having some image handles on the data stack. The
> task is to assign these to one of two variables ( FIRST and SECOND ),
> given some criteria. FIRST must be tried first, then SECOND. Rejected
> image handles are dropped. The number of images is given in COUNT.
>
> A first pass solution:
>
> variable first
> variable second
>
> \ Aborts the processing if there are too few images.
> : too-few-images? ( -- )
> count @ 2< "Too few images." log-and-exit! ;
>
> \ Criteria for image handles
> : nil? ( tgt -- f )
> : newer? ( src tgt -- f ) ... ;
> : higher-priority? ( src tgt -- f ) ... ;
>
> \ Runs the criteria checks for an image against a variable.
> : replace? ( src tgt -- src tgt f )
> dup nil? if true exit then
> 2dup newer? if true exit then
> 2dup higher-priority? if true exit then
> false ;
> [...]
>
> This does the job, but:
>
> 1) REPLACE? could be tidied up with boolean short-circuiting,
> [...]
> One obvious factoring is to use a short-circuiting operator:
>
> : | ` if ` true ` exit ` then ; immediate
>
> With which REPLACE? becomes:
>
> : replace? ( src tgt -- src tgt f )
> dup nil? | 2dup newer? | 2dup higher-priority? | false ;
>
> Which is more readable.

I like this syntax, maybe with a different name for "|" because
it looks like a Backus-Naur notation in a context where it could
be, but isn't.

Short-circuiting logic has interested me at various times,
usually in the context of parsing. I've used an ELSES construct
inspired by Wil Baden's THENS.

http://www.umich.edu/~williams/archive/forth/strings/expr.html#templates

I also like his ANDIF and ORIF, which were mentioned IIRC in a
precursor of this thread.

Here's what REPLACE? would look like with ELSES:

: replace? ( src tgt -- src tgt f )
COND
dup nil? 0= IF
2dup newer? 0= IF
2dup higher-priority? 0= IF false
ELSES true THEN ;

In this situation I would probably use the same nonconsuming
stack pattern for NIL?, etc., that REPLACE? uses, which would
give

: replace? ( src tgt -- src tgt f )
COND
nil? 0= IF
newer? 0= IF
higher-priority? 0= IF false
ELSES true THEN ;

Caveat: UNTESTED.

-- David

BruceMcF

unread,
May 26, 2012, 5:49:08 PM5/26/12
to
On May 26, 5:17 pm, "David N. Williams" <willi...@umich.edu> wrote:

> Short-circuiting logic has interested me at various times,
> usually in the context of parsing.  I've used an ELSES construct
> inspired by Wil Baden's THENS.

> http://www.umich.edu/~williams/archive/forth/strings/expr.html#templates

> I also like his ANDIF and ORIF, which were mentioned IIRC in a
> precursor of this thread.


> Here's what REPLACE? would look like with ELSES:
>
>    : replace? ( src tgt -- src tgt f )
>      COND
>        dup nil? 0= IF
>        2dup newer? 0= IF
>        2dup higher-priority? 0= IF false
>        ELSES true THEN ;

THENS also integrates with ANDIF and ORIF, since you don't have to
count THENs (which nonconsuming nil?? newer?? higher-priority??), but
with only two shortcuts, its not all that critical:

: replace?? ( img1 img2 -- img1 img2 flag )
\ G* should img2 be replaced with img1?
COND nil?? ORIF newer?? ORIF higher-priority?? THENS ;

: replace?? ( img1 img2 -- img1 img2 flag )
\ G* should img2 be replaced with img1?
nil?? ORIF newer?? ORIF higher-priority?? THEN THEN ;

(NB. If using a mix of consuming and reporting flags, you need to keep
track, here I'm using one ? as a suffix for consuming and two ?? as a
suffix reporting.)

Josh Grams

unread,
May 26, 2012, 8:17:04 PM5/26/12
to
Arnold Doray wrote: <jpqmum$dbb$1...@dont-email.me>
> On Sat, 26 May 2012 12:44:12 +0000, Josh Grams wrote:
>
><snip>
>
> Thank you for your comments. BTW, the algorithm is incorrect, but I meant
> it just as a simple illustration.

I figured as much, but thought I'd point it out in case it mattered...

>> Some people go even farther and manipulate the rstack to return from
>> multiple words -- Michael Gassanenko (sp?) has written a bunch about
>> this. I find that leads to brittle code, but that may just be me.
>
> Samuel Falvo has described a similar style of programming he calls
> "Declarative" programming in Forth. I've not read any of Gassaneko's
> work,

Michael wraps the rstack manipulation into higher level control-flow
words to be used in specific arrangements for backtracking and
coroutine-sort-of-things and the like. It might be less brittle that
way, I dunno...I haven't ever really tried it.

> but Falvo also uses rstack manupilation. This isn't always possible
> because not all forths use the Rstack for storing return addresses. The
> portable way, as described by Andrew Haley, is to throw exceptions:
>
>: exit! if true throw then ;
>
> I find this brittle too, because it's hard to tell where the exception
> comes from. I developed (or more precisely, re-invented) this other sytle
> using | and -> as an alternative.

Yeah. Early exits seem to be useful; multiple-level exits often lead to
trouble.

--Josh

Arnold Doray

unread,
May 27, 2012, 6:21:10 AM5/27/12
to
On Sat, 26 May 2012 17:17:30 -0400, David N. Williams wrote:
>
> I like this syntax, maybe with a different name for "|" because it looks
> like a Backus-Naur notation in a context where it could be, but isn't.
>

Yes, perhaps ELSE> or UNLESS or ELSE-DO ??

> Short-circuiting logic has interested me at various times,
> usually in the context of parsing. I've used an ELSES construct
> inspired by Wil Baden's THENS.
>
> http://www.umich.edu/~williams/archive/forth/strings/expr.html#templates
>
> I also like his ANDIF and ORIF, which were mentioned IIRC in a precursor
> of this thread.
>
> Here's what REPLACE? would look like with ELSES:
>
> : replace? ( src tgt -- src tgt f )
> COND
> dup nil? 0= IF 2dup newer? 0= IF 2dup higher-priority? 0= IF
> false ELSES true THEN ;
>
> In this situation I would probably use the same nonconsuming stack
> pattern for NIL?, etc., that REPLACE? uses, which would give
>
> : replace? ( src tgt -- src tgt f )
> COND
> nil? 0= IF newer? 0= IF higher-priority? 0= IF false ELSES true
> THEN ;
>

Thank you for the link to your work. I scanned it and am trying to
understand the relative utility of COND ... IF .. ELSES vs the | and ->
style. Can you comment on this?

Wil Baden's name keeps cropping up. Is there a link(s) to his work? Or is
it spread all over Usenet?

Cheers,
Arnold



Arnold Doray

unread,
May 27, 2012, 6:47:14 AM5/27/12
to
On Sat, 26 May 2012 10:25:57 -0700, BruceMcF wrote:

> : replaced-img ( img1 addr -- img2 )
>    TUCK @ replace? IF SWAP ROT ! ELSE 2DROP THEN ;
>

Your REPLACED-IMG is a bit like ASSIGN? in my second go at the problem. I
didn't like it because it was doing too much -- testing and acting (ie,
it has a side-effect).

I am coming to think that a more "functional" approach is better for
Forth. For example, as Josh Grams pointed out, ASSIGN? (and REPLACED-IMG)
are faulty -- the first image should be demoted instead of being
overwritten. With a word with side effects, this is harder to accomplish
without breaking something (not of course, in this simple example). It
was factoring this using a boolean shortcircuit that forced me to come up
with -> (or what you call |IF )

What I don't like about |if and |else is that they both force you to
return a flag to maintain stack consistency downstream. But the
alternative eg:

: |else ` if ` exit ` then ; immediate \ no return value!

is unpalatible since the predicates become more complex. Eg, in REPLACE?

: replace? ( tgt src -- tgt src f )
dup nil? dup |else drop
2dup newer? dup |else drop
2dup higher-priority? dup |else drop
false ;

Which is unacceptable, IMHO.

Cheers,
Arnold



BruceMcF

unread,
May 27, 2012, 10:00:02 AM5/27/12
to
On May 27, 6:47 am, Arnold Doray <inva...@invalid.com> wrote:
> On Sat, 26 May 2012 10:25:57 -0700, BruceMcF wrote:
> > : replaced-img ( img1 addr -- img2 )
> >    TUCK @ replace? IF SWAP ROT ! ELSE 2DROP THEN ;
>
> Your REPLACED-IMG is a bit like ASSIGN? in my second go at the problem. I
> didn't like it because it was doing too much -- testing and acting (ie,
> it has a side-effect).

REPLACED-IMG is storing one of two items and retaining the unstored
item on the stack. That's a fairly fundamental operation with a
selective store.

> I am coming to think that a more "functional" approach is better for
> Forth. For example, as Josh Grams pointed out, ASSIGN? (and REPLACED-IMG)
> are faulty -- the first image should be demoted instead of being
> overwritten. With a word with side effects, this is harder to accomplish
> without breaking something (not of course, in this simple example). It
> was factoring this using a boolean shortcircuit that forced me to come up
> with -> (or what you call |IF )

> What I don't like about |if and |else is that they both force you to
> return a flag to maintain stack consistency downstream.

You'll note that in my version, it only was used in lower level code
in a general test that is *generating* a flag from more specific
tests. In that context, the flags are already there.

But I didn't generate a flag in replaced-img in order to be able to
use boolean short-cutting: "store one and keep the other on stack"
doesn't need a flag to work as a factor.

> But the
> alternative eg:
>
> : |else ` if ` exit ` then ; immediate \ no return value!

Horses for courses. If you are not composing a flag, don't use |if and
|else.

Arnold Doray

unread,
May 27, 2012, 1:20:18 PM5/27/12
to
On Sun, 27 May 2012 07:00:02 -0700, BruceMcF wrote:

>> On Sat, 26 May 2012 10:25:57 -0700, BruceMcF wrote:
>> > : replaced-img ( img1 addr -- img2 )
>> >    TUCK @ replace? IF SWAP ROT ! ELSE 2DROP THEN ;
>>
>> Your REPLACED-IMG is a bit like ASSIGN? in my second go at the problem.
>> I didn't like it because it was doing too much -- testing and acting
>> (ie, it has a side-effect).
>
> REPLACED-IMG is storing one of two items and retaining the unstored item
> on the stack. That's a fairly fundamental operation with a selective
> store.

Thank you for pointing that out. I should have read your BETTER-IMAGES
more carefully to understand REPLACED-IMG's intent. It's a much better
solution than the one I've desctibed. Not to be pedantic, it should
be ... 2DROP @ THEN ;


Cheers,
Arnold






BruceMcF

unread,
May 27, 2012, 6:03:26 PM5/27/12
to
Be pedantic, and that source is wrong, but I think the correction is:
... ( addr img1 img2 ) DROP NIP ( img1 ) ...

... in ELSE part, if not replacing, then you want to keep img1 on the
stack.

Josh Grams

unread,
May 28, 2012, 10:24:42 AM5/28/12
to
I was writing some code today which reminded me of the other shortcoming
of conditional EXIT words: often you want to do some cleanup first. To
use conditional EXITs, you have to be careful to have the stack effect
the same at each exit point and clean up in a parent word. I usually
find it easier to just write the `IF ... flag EXIT THEN` out in full.

Or you may want to put values on the rstack while you call the words
which compute the flags, and then to use conditional exits you have to
pull the stuff off the rstack and bring the flag back to the top first.

So there are some simple situations where conditional exits are useful,
but in Forth the parameters on the stack often get in the way. This
doesn't happen in languages with named parameters.

--Josh

BruceMcF

unread,
May 28, 2012, 1:27:01 PM5/28/12
to
On May 28, 10:24 am, Josh Grams <j...@qualdan.com> wrote:
> I was writing some code today which reminded me of the other shortcoming
> of conditional EXIT words: often you want to do some cleanup first.

Yes, a similar thing with short-circuiting booleans for loop tests ~
if there is clean-up required then with |andif as a short-circuit exit
on true and |orif as a short-circuit exit on false:
BEGIN test1? |andif test2? WHILE process REPEAT cleanup
and:
BEGIN process test1? |orif test2? UNTIL cleanup

... have to be carefully constructed so that the cleanup is only
required if exiting the loop on the second test.

Which is why:
... ( flag ) ANDIF ... THEN ...
... ( flag ) ORIF ... THEN ...

... is a more general approach to boolean shortcutting.

Andrew Haley

unread,
May 28, 2012, 1:41:40 PM5/28/12
to
Josh Grams <jo...@qualdan.com> wrote:

> I was writing some code today which reminded me of the other
> shortcoming of conditional EXIT words: often you want to do some
> cleanup first. To use conditional EXITs, you have to be careful to
> have the stack effect the same at each exit point and clean up in a
> parent word. I usually find it easier to just write the
> `IF ... flag EXIT THEN` out in full.

This might be a mistake. There's some discussion of this in Thinking
FORTH: the idea is that you should avoid having to make the same
decision twice.

Sometimes you find yourself deciding that something is true, doing
something as a result, then passing a flag to indicate what your
decision was. The caller tests that flag and does some action. Maybe
you should instead do the action as soon as you have decided that the
condition is true. That way, you don't have to pass the flag.

Complex conditionals are sometimes a sign that something has gone
wrong, and refactoring is needed, perhaps by turning code into
decision tables or execution vectors.

> Or you may want to put values on the rstack while you call the words
> which compute the flags, and then to use conditional exits you have
> to pull the stuff off the rstack and bring the flag back to the top
> first.
>
> So there are some simple situations where conditional exits are
> useful, but in Forth the parameters on the stack often get in the
> way. This doesn't happen in languages with named parameters.

Indeed not, but programs in profane languages often have all this
nested conditional stuff in ways that well-written Forth doesn't.

Andrew.

Elizabeth D. Rather

unread,
May 28, 2012, 1:55:19 PM5/28/12
to
On 5/28/12 7:41 AM, Andrew Haley wrote:
> Josh Grams<jo...@qualdan.com> wrote:
>
>> I was writing some code today which reminded me of the other
>> shortcoming of conditional EXIT words: often you want to do some
>> cleanup first. To use conditional EXITs, you have to be careful to
>> have the stack effect the same at each exit point and clean up in a
>> parent word. I usually find it easier to just write the
>> `IF ... flag EXIT THEN` out in full.
>
> This might be a mistake. There's some discussion of this in Thinking
> FORTH: the idea is that you should avoid having to make the same
> decision twice.
>
> Sometimes you find yourself deciding that something is true, doing
> something as a result, then passing a flag to indicate what your
> decision was. The caller tests that flag and does some action. Maybe
> you should instead do the action as soon as you have decided that the
> condition is true. That way, you don't have to pass the flag.
>
> Complex conditionals are sometimes a sign that something has gone
> wrong, and refactoring is needed, perhaps by turning code into
> decision tables or execution vectors.

This is excellent advice. It's equally true that complicated stack
management is a sign that refactoring is in order.

>> Or you may want to put values on the rstack while you call the words
>> which compute the flags, and then to use conditional exits you have
>> to pull the stuff off the rstack and bring the flag back to the top
>> first.
>>
>> So there are some simple situations where conditional exits are
>> useful, but in Forth the parameters on the stack often get in the
>> way. This doesn't happen in languages with named parameters.
>
> Indeed not, but programs in profane languages often have all this
> nested conditional stuff in ways that well-written Forth doesn't.

Yes. One of the points that Thinking Forth makes is that by learning
good stylistic practices (simplifying logic, minimizing flag passing,
etc.) you become a better programmer in other languages as well.

Cheers,
Elizabeth

--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================

Arnold Doray

unread,
May 29, 2012, 5:18:20 AM5/29/12
to
In these scenarios, the solution is to factor out the tests into a new
word. The behaviour is then obvious.

: passed? test1? | test2? ;

... BEGIN process passed? UNTIL cleanup ...

I find that although ANDIF/ORIF ... THEN are more general, but (IMHO)
less easy to use and read.

Of course, the method above means that you get a proliferation of new
words. That is usually not an issue if your code is broken into separate
small modules. Also, I find the factorization is often meaningful, and
results in code that is easier to read in the long run.

Cheers,
Arnold

Arnold Doray

unread,
May 29, 2012, 7:34:47 AM5/29/12
to
On Mon, 28 May 2012 14:24:42 +0000, Josh Grams wrote:

> I was writing some code today which reminded me of the other shortcoming
> of conditional EXIT words: often you want to do some cleanup first. To
> use conditional EXITs, you have to be careful to have the stack effect
> the same at each exit point and clean up in a parent word. I usually
> find it easier to just write the `IF ... flag EXIT THEN` out in full.
>
> Or you may want to put values on the rstack while you call the words
> which compute the flags, and then to use conditional exits you have to
> pull the stuff off the rstack and bring the flag back to the top first.
>

Yes. I've come across both situations, and I think the practical way to
use short-circuiting is if the complex operations are packaged into a
word.

This makes for shorter words, but more words, as you observed before.


Arnold Doray

unread,
May 29, 2012, 9:53:42 AM5/29/12
to
On Mon, 28 May 2012 14:24:42 +0000, Josh Grams wrote:

Here's an example involving cleanup:

You have an image, and the task is to retrieve the image's timestamp
using OCR. To do this, there are two words available.

: img2int ( img n1 n2 n3 n4 -- n f ) ... ;
: ymdhm ( y m d h m -- time ) ... ;

The word IMG2INT takes 4 coordinates on the image, which enclose an
integer, crops the image to those coordinates, runs the OCR program and
parses the resulting string into an integer. The return value is the
integer and a flag indicating success/failure.

YMDHM takes 5 integers (year, month, day, hour, minute) and converts this
to an internal time format. A time of 0 could indicate an OCR failure.

The images come from different sources, so the coordinates n1..n4 would
vary by image source.

Here's a solution using the shortcircuit operators:

: | ` if ` true ` exit ` then ; immediate
: -> ` not ` if ` exit ` then ; immediate

: year ( img n1 n2 n3 n4 -- m f )
img2int | drop 0 false ;

: month ( img n1 n2 n3 n4 -- m f )
img2int | drop drop 0 false ;

: day ( img n1 n2 n3 n4 -- m f )
img2int | drop drop drop 0 false ;

: hour ( img n1 n2 n3 n4 -- m f )
img2int | drop drop drop drop 0 false ;

: minute ( img n1 n2 n3 n4 -- m f )
img2int | drop drop drop drop drop 0 false ;

\ Gets timestamps from image source XYZ
: timestamp-xyz { img } ( img -- time )
img 91 13 158 35 year ->
img 158 13 194 35 month ->
img 194 13 233 35 day ->
img 233 13 268 35 hour ->
img 277 13 310 35 minute ->
ymdhm ;

\ Gets timestamps from image source JKL
: timestamp-jkl { img } ( img -- time )
img 170 13 245 40 year ->
img 245 13 289 40 month ->
img 289 13 331 40 day ->
img 331 13 372 40 hour ->
img 381 13 420 40 minute ->
ymdhm ;

As you can see the cleanup is done in the words like YEAR, MONTH etc, and
factoring it this way looks natural IMHO.

Yet, as others have noted, passing the flag is an inefficiency, since we
re-test the same flag downstream.

Cheers,
Arnold








Alex McDonald

unread,
May 29, 2012, 10:02:09 AM5/29/12
to
Would CATCH/THROW not be easier? (And shorter?)

Andrew Haley

unread,
May 29, 2012, 10:52:34 AM5/29/12
to
What's going on here? MINUTE takes an image, converts it to a number,
tests if it's OK, then drops a lot of stuff. Where did that stuff you
just dropped come from?

Andrew.

Gerry Jackson

unread,
May 29, 2012, 12:49:58 PM5/29/12
to
Why not simply do:
(mostly untested)

: ndrop ( xn ... x1 n -- ) \ n > 0
0 do drop loop
;

: timestamp-jkl { img } ( img -- time )
case
img 170 13 245 40 img2int false of 1 endof \ Year
img 245 13 289 40 img2int false of 2 endof \ Month
img 289 13 331 40 img2int false of 3 endof \ Day
img 331 13 372 40 img2int false of 4 endof \ Hour
img 381 13 420 40 img2int false of 5 endof \ Minute
drop ymdhm exit
endcase
ndrop 0
;

As far as I can see the ANS Forth standard doesn't forbid the same
number/boolean being tested for in the same CASE statement, therefore
the above is valid Forth.

Perhaps I've misunderstood the logic of your program.

--
Gerry

Gerry Jackson

unread,
May 29, 2012, 2:06:43 PM5/29/12
to
Oops

I've just realised that I omitted a DROP after the first four ENDOFs.
It should be:

: timestamp-jkl { img } ( img -- time )
case
img 170 13 245 40 img2int false of 1 endof drop \ Year
img 245 13 289 40 img2int false of 2 endof drop \ Month
img 289 13 331 40 img2int false of 3 endof drop \ Day
img 331 13 372 40 img2int false of 4 endof drop \ Hour
img 381 13 420 40 img2int false of 5 endof drop \ Minute
ymdhm exit
endcase
ndrop 0
;



--
Gerry

Albert van der Horst

unread,
May 29, 2012, 4:25:44 PM5/29/12
to
In article <jq2kd5$f64$1...@dont-email.me>,
More natural to me in this case is exceptions.

: img2int img2int INVERT IF 1017 THROW THEN ;
: doit 'xyz CATCH DUP 1017 = IF jkl ELSE THROW THEN ;

>As you can see the cleanup is done in the words like YEAR, MONTH etc, and
>factoring it this way looks natural IMHO.

There is nothing to clean up with exceptions.

>
>Yet, as others have noted, passing the flag is an inefficiency, since we
>re-test the same flag downstream.

Yes, those zillion flags are ugly.

>
>Cheers,
>Arnold


Groetjes Albert

--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Arnold Doray

unread,
May 29, 2012, 9:45:46 PM5/29/12
to
On Tue, 29 May 2012 07:02:09 -0700, Alex McDonald wrote:

>
> Would CATCH/THROW not be easier? (And shorter?)

Probably my misunderstanding of how exceptions work in forth. In the
forth I wrote, THROW simply sets an exception flag, which forces all
words downstream to return immediately until it meets CATCH. THROW
doesn't clear the stack.

In any case, there are other items on the stack that should not be
cleared.

Could you elaborate on the solution you're thinking of?

Cheers,
Arnold

Arnold Doray

unread,
May 29, 2012, 9:48:35 PM5/29/12
to
If IMG2INT in MINUTE fails, MINUTE needs to clear the integers put on the
stack by YEAR MONTH etc. If IMG2INT succeeds, the short-circuiting
operator | returns to the caller.


BruceMcF

unread,
May 29, 2012, 11:02:21 PM5/29/12
to
On May 29, 9:45 pm, Arnold Doray <inva...@invalid.com> wrote:
> On Tue, 29 May 2012 07:02:09 -0700, Alex McDonald wrote:
>
> > Would CATCH/THROW not be easier? (And shorter?)

> Probably my misunderstanding of how exceptions work in forth. In the
> forth I wrote, THROW simply sets an exception flag, which forces all
> words downstream to return immediately until it meets CATCH. THROW
> doesn't clear the stack.

It should. To THROW an exception the return and data stack should
return to the depth when CATCH executed the xt, plus the non-zero
throw code. The stack frame often includes the data stack depth when
CATCH executed and the return stack depth of the previous CATCH, and
the return stack depth of the stack frame stored in a variable, plus
whatever info you need to restore input state.

Arnold Doray

unread,
May 30, 2012, 12:27:17 AM5/30/12
to
What happens if the word activated by CATCH consumes the stack below the
level of that on the depth during the time CATCH executed? Does CATCH
save a snapshot of the data & return stack? This isn't what Gforth does:

: a drop 777 throw ; ok
: b ['] a catch ; ok
1 2 3 b ok
.S <4> 1 2 4428054488 777 ok

I'm probably missing something , but this kind of exception propagation
seems more trouble than it is worth....

Cheers,
Arnold





Gerry Jackson

unread,
May 30, 2012, 2:55:28 AM5/30/12
to
Well if someone as experienced as Andrew failed to understand that, your
claim that the short-circuiting operators make things clearer, more
readable etc is wrong.

--
Gerry

Andrew Haley

unread,
May 30, 2012, 3:56:05 AM5/30/12
to
> If IMG2INT in MINUTE fails, MINUTE needs to clear the integers put on the
> stack by YEAR MONTH etc. If IMG2INT succeeds, the short-circuiting
> operator | returns to the caller.

But that's terrible factoring: it means that MINUTE can oly be used in
that context! What sense does that make?

Andrew.

Alex McDonald

unread,
May 30, 2012, 5:09:52 AM5/30/12
to
Try it with a stack diagram.

: dropit ( n -- ) drop 777 throw ;
: testit ( a b c d -- ) ['] dropit catch if ( a b c ? ) else ( a b c )
then ... ;

The question mark indicates values you can't trust; the depth is as
before the execution, and the untrustworthy values as those for the
stack diagram on the word CATCHed.

Arnold Doray

unread,
May 30, 2012, 7:07:27 AM5/30/12
to
Understood. But it looks pretty brittle to me, especially if multiple
exceptions could be thrown or at a much higher upstream the calling
hierarchy. For example, if there were say five THROWS upstream you'd be
hard pressed trying to clean up the mess at CATCH, even with your stack
diagrams. Ditto for an exception thrown in another module in some private
word. And to top it all, you're left to clean up a mess far from where it
was created. Simply horrible. I'm guessing this scheme works best when
the stack is not consumed, but added to upstream.

My instinct was to not bother cleaning up the stack because of these
ambiguities. It's blatantly non-standard of course, but I am more
concerned about utility. IMHO the mess should be cleaned up at THROW, not
at CATCH.

However, I am certain the standard is well thought out, and I am missing
something.

Cheers,
Arnold





Arnold Doray

unread,
May 30, 2012, 7:31:40 AM5/30/12
to
Not so "horrible" because MINUTE is reused in the same context again and
again with different image types. And MINUTE only makes sense when used
in conjunction with YEAR, etc. And finally, they are "private" words that
are forgotten outside this module.

But it's not ideal. As others have pointed out, using THROW/CATCH is
probably better:

: exit! if true throw then ;
: & img2int not exit! ;

: grab-timestamp-jkl { img } ( img -- y m d h m | error )
img 170 13 245 40 &
img 245 13 289 40 &
img 289 13 331 40 &
img 331 13 372 40 &
img 381 13 420 40 & ;

: timestamp-jkl ( img -- time )
['] grab-timestamp-jkl catch if 0 else ymdhm then ;

But there is the inconvenience of having to use 2 words instead of one.

Cheers,
Arnold






Andrew Haley

unread,
May 30, 2012, 7:39:22 AM5/30/12
to
Arnold Doray <inv...@invalid.com> wrote:
> On Tue, 29 May 2012 20:02:21 -0700, BruceMcF wrote:
>

>> It should. To THROW an exception the return and data stack should return
>> to the depth when CATCH executed the xt, plus the non-zero throw code.
>> The stack frame often includes the data stack depth when CATCH executed
>> and the return stack depth of the previous CATCH, and the return stack
>> depth of the stack frame stored in a variable, plus whatever info you
>> need to restore input state.
>
> What happens if the word activated by CATCH consumes the stack below the
> level of that on the depth during the time CATCH executed?

You'll have garbage on the stack: CATCH just preserves the depth.
This is exactly what you want, since you can DROP however many items
were on the stack at the point of the CATCH.

Andrew.

Alex McDonald

unread,
May 30, 2012, 7:38:56 AM5/30/12
to
Why so? If the stack diagram of the word I'm catching is TRUBBLE ( a b
-- c ) then ( x a b ) ' TRUBBLE CATCH will produce either ( x a b --
x ? ? nonzero ) or ( x a b -- x c zero ) regardless of where the THROW
occurs.

If that's not the case and you get back ( x a b -- ? ? ? nonzero ),
then your stack diagram ( a b -- c ) is wrong, and the word doesn't
act on the stack as the stack diagram claims it does. That's
regardless of what the word does in terms of calling other words that
might do their own THROWs; in this case, it should have been ( a b c
-- d e ).

>
> My instinct was to not bother cleaning up the stack because of these
> ambiguities. It's blatantly non-standard of course, but I am more
> concerned about utility. IMHO the mess should be cleaned up at THROW, not
> at CATCH.

There's no mess to clean up, apart from a known-in-advance-number of
corrupted stack entries that need dropped or replaced. Cleaning up on
THROW can be -- as your short circuits are demonstrating -- horribly
messy.

Andrew Haley

unread,
May 30, 2012, 7:46:28 AM5/30/12
to
Arnold Doray <inv...@invalid.com> wrote:

> Understood. But it looks pretty brittle to me, especially if
> multiple exceptions could be thrown or at a much higher upstream the
> calling hierarchy. For example, if there were say five THROWS
> upstream you'd be hard pressed trying to clean up the mess at CATCH,
> even with your stack diagrams.

This doesn't make sense. The stack is restored to whatever depth is
was at CATCH.

> Ditto for an exception thrown in another module in some private
> word.

Exceptions are part of a word's API: any caller must know what they
throw. You have to catch the exceptions.

> And to top it all, you're left to clean up a mess far from
> where it was created. Simply horrible. I'm guessing this scheme
> works best when the stack is not consumed, but added to upstream.

There really is no problem.

Look how much easier it is:

xx constant scan-error

// img2int reuturns either 0 (it's OK) or a scanning error code.

: img2int ( img n1 n2 n3 n4 -- n t ) ... ;
: ymdhm ( y m d h m -- time ) ... ;

: ocr ( img n1 n2 n3 n4 -- m f )
img2int throw ;

\ Gets timestamps from image source XYZ
: (timestamp-xyz) { img } ( img -- time )
img 91 13 158 35 ocr
img 158 13 194 35 ocr
img 194 13 233 35 ocr
img 233 13 268 35 ocr
img 277 13 310 35 ocr
ymdhm ;

: timestamp-xyz { img } ( img -- time )
['] (timestamp-xyz) catch
dup scanning-error = if drop 0 else throw then ;

Andrew.

Andrew Haley

unread,
May 30, 2012, 7:53:09 AM5/30/12
to
Arnold Doray <inv...@invalid.com> wrote:
> On Wed, 30 May 2012 02:56:05 -0500, Andrew Haley wrote:=
>
> Not so "horrible" because MINUTE is reused in the same context again and
> again with different image types. And MINUTE only makes sense when used
> in conjunction with YEAR, etc. And finally, they are "private" words that
> are forgotten outside this module.
>
> But it's not ideal. As others have pointed out, using THROW/CATCH is
> probably better:
>
> : exit! if true throw then ;
> : & img2int not exit! ;

That should be just img2int throw .

>
> : grab-timestamp-jkl { img } ( img -- y m d h m | error )
> img 170 13 245 40 &
> img 245 13 289 40 &
> img 289 13 331 40 &
> img 331 13 372 40 &
> img 381 13 420 40 & ;
>
> : timestamp-jkl ( img -- time )
> ['] grab-timestamp-jkl catch if 0 else ymdhm then ;
>
> But there is the inconvenience of having to use 2 words instead of one.

That's probably not a bad thing, in general. But we have our new
construct:

: grab-timestamp-jkl { img } ( img -- y m d h m | error )
[: img 170 13 245 40 &
img 245 13 289 40 &
img 289 13 331 40 &
img 331 13 372 40 &
img 381 13 420 40 & ;]
catch ...

Andrew.

Alex McDonald

unread,
May 30, 2012, 8:04:44 AM5/30/12
to
On May 30, 12:53 pm, Andrew Haley <andre...@littlepinkcloud.invalid>
wrote:
Reminds me of try blocks.

Arnold Doray

unread,
May 30, 2012, 9:52:22 AM5/30/12
to
On Wed, 30 May 2012 06:46:28 -0500, Andrew Haley wrote:

>> Understood. But it looks pretty brittle to me, especially if multiple
>> exceptions could be thrown or at a much higher upstream the calling
>> hierarchy. For example, if there were say five THROWS upstream you'd be
>> hard pressed trying to clean up the mess at CATCH,
>> even with your stack diagrams.
>
> This doesn't make sense. The stack is restored to whatever depth is was
> at CATCH.
>
>> Ditto for an exception thrown in another module in some private word.
>
> Exceptions are part of a word's API: any caller must know what they
> throw. You have to catch the exceptions.

What I am having trouble understanding is the necessity to put junk on
the stack once it is consumed below the levels prior to CATCH being
issued. This junk invariably has to be cleared for the program to
proceed, so why bother putting it in the first place?

Alex McDonald has given a simple mechanism for junk-clearing by looking
at the stack comments, but it doesn't work well for a THROW issued right
up the hierarchy ( too much to track ) or if there are multiple THROWS
that could be thrown ( multiple failure modes ). And if there really is a
need to put in junk, why not consistently put in 0s? That at least makes
for consistent behaviour, simplifying debugging. But all this is besides
the point. Why have junk at all? What's so special about stack depth that
it needs to be preserved???

Stack clearing on THROW is certainly very useful in one narrow context --
when the stack before pre-CATCH is never disturbed, but that seems
unrealistic in the vast majority of scenarious where THROW...CATCH is
useful.

What might be useful is saving a copy of the data & return stacks and
restoring them on error. But that's a lot of overhead of course and
entirely unpracticable in constrained environments. Which is why my
intuition says that the sensible thing is to do nothing, and accept that
cleanup has to be done manually.

Cheers,
Arnold











Andrew Haley

unread,
May 30, 2012, 10:02:23 AM5/30/12
to
Arnold Doray <inv...@invalid.com> wrote:
> On Wed, 30 May 2012 06:46:28 -0500, Andrew Haley wrote:
>
>>> Understood. But it looks pretty brittle to me, especially if multiple
>>> exceptions could be thrown or at a much higher upstream the calling
>>> hierarchy. For example, if there were say five THROWS upstream you'd be
>>> hard pressed trying to clean up the mess at CATCH,
>>> even with your stack diagrams.
>>
>> This doesn't make sense. The stack is restored to whatever depth is was
>> at CATCH.
>>
>>> Ditto for an exception thrown in another module in some private word.
>>
>> Exceptions are part of a word's API: any caller must know what they
>> throw. You have to catch the exceptions.
>
> What I am having trouble understanding is the necessity to put junk on
> the stack once it is consumed below the levels prior to CATCH being
> issued. This junk invariably has to be cleared for the program to
> proceed, so why bother putting it in the first place?

Because at the point of the CATCH, you know how many items you put on
the stack. You don't know how many items were on the stack at the
point of the THROW. It would be unusuable any other way.

> Alex McDonald has given a simple mechanism for junk-clearing by looking
> at the stack comments, but it doesn't work well for a THROW issued right
> up the hierarchy ( too much to track ) or if there are multiple THROWS
> that could be thrown ( multiple failure modes ). And if there really is a
> need to put in junk, why not consistently put in 0s?

Because it's a waste of time.

> That at least makes for consistent behaviour, simplifying
> debugging. But all this is besides the point. Why have junk at all?
> What's so special about stack depth that it needs to be preserved???

Otherwise you'd get a stack underflow or overflow.

> Stack clearing on THROW is certainly very useful in one narrow
> context -- when the stack before pre-CATCH is never disturbed, but
> that seems unrealistic in the vast majority of scenarious where
> THROW...CATCH is useful.

No, the whole idea of resetting the stack on a throw is that you don't
need to know what nappens between a CATCH and a THROW .

> What might be useful is saving a copy of the data & return stacks and
> restoring them on error. But that's a lot of overhead of course and
> entirely unpracticable in constrained environments. Which is why my
> intuition says that the sensible thing is to do nothing, and accept that
> cleanup has to be done manually.

Why? You only have to remove what you put there, and let the system
do the rest.

Andrew.

Andrew Haley

unread,
May 30, 2012, 10:03:55 AM5/30/12
to
Alex McDonald <bl...@rivadpm.com> wrote:

> On May 30, 12:53?pm, Andrew Haley <andre...@littlepinkcloud.invalid>
> wrote:
>> we have our new construct:
>>
>> : grab-timestamp-jkl { img } ( img -- y m d h m | error )
>> [: img 170 13 245 40 &
>> img 245 13 289 40 &
>> img 289 13 331 40 &
>> img 331 13 372 40 &
>> img 381 13 420 40 & ;]
>> catch ...
>
> Reminds me of try blocks.

That's no accident!

Andrew.

Arnold Doray

unread,
May 30, 2012, 10:13:18 AM5/30/12
to
On Wed, 30 May 2012 09:02:23 -0500, Andrew Haley wrote:

> No, the whole idea of resetting the stack on a throw is that you don't
> need to know what nappens between a CATCH and a THROW .

That's just the point -- you DO need to know. You have to know to react
to potential junk on your stack after CATCH.

Alex McDonald

unread,
May 30, 2012, 10:41:46 AM5/30/12
to
You don't need to know any more than the stack sig. If it's ( a b c --
whatever ) then on a non-zero return from CATCH, a b and c are
potentially junk. The reaction to junk should be to just drop it.

Why are they there? Why not have it done automatically? Because the
compiler doesn't know the stack effects of what is being executed by
CATCH; all it knows is the stack depth at that point in the execution,
which it faithfully restores on a THROW. On the other hand, the
programmer knows (or should know if it's to work as advertised) the
stack effects of the called word, but doesn't know the stack depth at
the point of execution of the word.

If you need what you sent to the offending word, DUP it.

: SENDIT ( addr len -- ) ...long involved stuff that might fail... if
THROW then ;
: TRYSENDINGIT ( addr len -- ) 2dup ['] sendit catch if 2drop type ."
couldn't be sent" else type ." was delivered" then ;

Andrew Haley

unread,
May 30, 2012, 12:46:41 PM5/30/12
to
No, you don't. You just DROP whatever you put there before you called
CATCH .

Andrew.

Albert van der Horst

unread,
May 31, 2012, 7:54:41 AM5/31/12
to
In article <jq58ml$25h$1...@dont-email.me>,
Arnold Doray <inv...@invalid.com> wrote:
<SNIP>
>
>Stack clearing on THROW is certainly very useful in one narrow context --
>when the stack before pre-CATCH is never disturbed, but that seems
>unrealistic in the vast majority of scenarious where THROW...CATCH is
>useful.

Have you experience using Forth's CATCH THROW? Probably not, because
the above is patently untrue.

I have experience and this is what I would say:

"
One would be hard pressed to come up with a realistic example where the
stack after a CATCH is disturbed beyond the data passed to the
word that is executed.
"

What do you have in mind?
Do you think CATCH is used to recover from program errors
where you accidentally underflow the data stack?

<SNIP>

>
>Cheers,
>Arnold

Arnold Doray

unread,
May 31, 2012, 11:17:34 AM5/31/12
to
On Thu, 31 May 2012 11:54:41 +0000, Albert van der Horst wrote:

>
> Have you experience using Forth's CATCH THROW? Probably not, because the
> above is patently untrue.
>
> I have experience and this is what I would say:
>
> "
> One would be hard pressed to come up with a realistic example where the
> stack after a CATCH is disturbed beyond the data passed to the word that
> is executed.
> "
>
> What do you have in mind?
> Do you think CATCH is used to recover from program errors where you
> accidentally underflow the data stack?
>


I have just come from the Java world, where deep exceptions (those thrown
well up in the calling hierarchy) are the norm. Java deals with this by
allowing the programmer to trace the exception's propagation through the
call hierarchy, so you can see immediately what went wrong. Deep
exceptions are a byproduct of modular programming / programming with
libraries.

The "problem" I see with THROW's behaviour is that just *before* an
exception is thrown, the stack could have been:

A) Added to -- in which case THROW does a useful thing by dropping all
the stuff generated by the XT that CATCH executed.

B) Depleted -- in which you get junk on the stack. You have to manually
clean these or drop the entire stack.

C) Muddled (ie, items added to / removed ) -- in which case you are
likely to just drop the entire stack.

I guess in B and C, you would just drop the entire stack? IMHO, B and C
are likely to occur in larger bodies of code.

And A would only happen when you knew exactly where those exceptions
would be thrown (ie, code you wrote yourself, not locked away in a
library). Is this the right way of seeing it?

Cheers,
Arnold











Andrew Haley

unread,
May 31, 2012, 12:17:16 PM5/31/12
to
No, that's wrong. I think an example is needed.

Let's say you have a word FOO that takes three cells and returns one.
Like this:

: foo ( n1 n2 n3 - n) ... ;

However, FOO occasionally throws an exception, so you want to call it
via a CATCH. If the word FOO succeeds, you print its result,
otherwise you print "err".

The code is:

: foo ( n1 n2 n3 - n)
rot
random 10 mod 0= throw
rot rot
random 10 mod 0= throw
+ *
random 10 mod 0= throw ;

: sum
['] foo catch if drop drop drop ." err" else . then ;

This code is right regardless of whether the stack is depeted, added
to, or muddled. It does not protect you from FOO taking things off
the stack that it shouldn't. FOO can throw an exception if the stack
is muddled, or when it's depleted.

So, we put a couple of numbers on the stack.

66 77 ok

9 3 4 sum err ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum err ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum err ok
9 3 4 sum err ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum err ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum err ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
9 3 4 sum 63 ok
.s
66 77 <-Top ok

Andrew.

BruceMcF

unread,
May 31, 2012, 12:18:35 PM5/31/12
to
And since you have checked the throw code, you know what potential
junk there may be. If the xt takes ( ca1 ca2 u ) and the exception you
are looking at has consumed ca1 and u and ca2 is still there, you know
that on the exception return, the stack is ( ca1 x1 x2 throw# )

Josh Grams

unread,
May 31, 2012, 12:45:15 PM5/31/12
to
Arnold Doray wrote: <jq822d$4a8$1...@dont-email.me>
> The "problem" I see with THROW's behaviour is that just *before* an
> exception is thrown, the stack could have been:
>
> A) Added to -- in which case THROW does a useful thing by dropping all
> the stuff generated by the XT that CATCH executed.

Yes.

> B) Depleted -- in which you get junk on the stack. You have to manually
> clean these or drop the entire stack.
>
> C) Muddled (ie, items added to / removed ) -- in which case you are
> likely to just drop the entire stack.

Yes, but only to a known depth. Forth words never disturb the stack
beyond their arguments. If they used deeper elements in the stack,
those would also be considered arguments and would (hopefully) be
declared in the stack effect.

So in B and C, when THROW restores the depth, all you have to do is drop
the (now muddled) arguments to the word you executed with CATCH and
then you can continue.

--Josh

BruceMcF

unread,
May 31, 2012, 12:58:07 PM5/31/12
to
On May 31, 11:17 am, Arnold Doray <inva...@invalid.com> wrote:
> On Thu, 31 May 2012 11:54:41 +0000, Albert van der Horst wrote:
>
>
>
>
>
>
>
>
>
>
>
> > Have you experience using Forth's CATCH THROW? Probably not, because the
> > above is patently untrue.
>
> > I have experience and this is what I would say:
>
> > "
> > One would be hard pressed to come up with a realistic example where the
> > stack after a CATCH is disturbed beyond the data passed to the word that
> > is executed.
> > "
>
> > What do you have in mind?
> > Do you think CATCH is used to recover from program errors where you
> > accidentally underflow the data stack?
>
> I have just come from the Java world, where deep exceptions (those thrown
> well up in the calling hierarchy) are the norm. Java deals with this by
> allowing the programmer to trace the exception's propagation through the
> call hierarchy, so you can see immediately what went wrong. Deep
> exceptions are a byproduct of modular programming / programming with
> libraries.
>
> The "problem" I see with THROW's behaviour is that just *before* an
> exception is thrown, the stack could have been:

> A) Added to -- in which case THROW does a useful thing by dropping all
> the stuff generated by the XT that CATCH executed.

> B) Depleted -- in which you get junk on the stack. You have to manually
> clean these or drop the entire stack.

If the xt takes ( ca1 ca2 u ) and exception 924 is thrown after ca2
and u have been consumed, "manually clean" means "2DROP". "Drop the
entire stack" implies you have not been bench testing corner cases as
you go, since you are trying to use exception returns to correct for
fundamentally unsound code.

> C) Muddled  (ie, items added to / removed ) -- in which case you are
> likely to just drop the entire stack.

Ditto ~ (C) and (B) are not normally special cases, unless the data on
the stack for (C) happens too be useful in the cleanup.

> I guess in B and C, you would just drop the entire stack?
No, for simplicity you can divide exception returns into those that
have consumed any or all of the xt's stack parameters, and those that
have not. In the first case, just drop all the xt's parameters and its
ok.

Anyway, lots of higher level words will be ( -- ) in which case
there's no "trash" to clean up.

> And A would only happen when you knew exactly where those exceptions
> would be thrown (ie, code you wrote yourself, not locked away in a
> library). Is this the right way of seeing it?

But then, either the library handles the exception itself, or should
document the state of the state with throw #294 out of ' FOO CATCH.

And if FOO has effects on the stack beyond its documented stack
parameters, don't use that library, its broken.

Arnold Doray

unread,
May 31, 2012, 1:40:39 PM5/31/12
to
On Thu, 31 May 2012 11:17:16 -0500, Andrew Haley wrote:

>>
>> B) Depleted -- in which you get junk on the stack. You have to manually
>> clean these or drop the entire stack.
>>
>> C) Muddled (ie, items added to / removed ) -- in which case you are
>> likely to just drop the entire stack.
>
> No, that's wrong. I think an example is needed.
>

<snip>

Crystal clear. Excellent example.

In a nutshell, CATCH's XT *consumes* its l.h.s. stack sig, so that on an
error return, those items must conservatively be considered junk because
of THROW's behaviour. And as long as all callers upstream stick to their
lhs stack sigs, this condition is never violated. It is impossible to get
junk farther down the stack. So you only need to DROP those items. That's
precisely why THROW needs to resize the stacks to their pre CATCH
conditions -- to make this guarantee. It's also why filling it with junk
is ok -- because you know exactly how much to drop, and that can't be
decided by the compiler beforehand. That's pretty smart and a very good
reason.

But of course, it doesn't protect you from bugs, as you say.

I realize now that a number of you (you, Alex, Bruce etc) were saying
exactly the same thing upthread, but the penny didn't drop until just
now. What also helped was that I just changed my THROW/CATCH to standard
Forth's behaviour and found that:

>
> : grab-timestamp-jkl { img } ( img -- y m d h m | error )
> img 170 13 245 40 &
> img 245 13 289 40 &
> img 289 13 331 40 &
> img 331 13 372 40 &
> img 381 13 420 40 & ;
>
> : timestamp-jkl ( img -- time )
> ['] grab-timestamp-jkl catch if 0 else ymdhm then ;

Should be:

: timestamp-jkl ( img -- time )
['] grab-timestamp-jkl catch if DROP 0 else ymdhm then ;

because img is consumed by GRAB-TIMESTAMP-JKL.

Thank you again for all your help. It is much appreciated.

Cheers,
Arnold

Albert van der Horst

unread,
May 31, 2012, 2:38:20 PM5/31/12
to
In article <jq822d$4a8$1...@dont-email.me>,
Yes. An example may clarify it further:

Let us imagine a situation where MY-THING has the stack-profile
( x1,x2,x3 -- y1 )

what we really would like to have upon a 17 THROW
1 2 3 a b c ' MY-THING CATCH is just
1 2 3 17 1]
i.e. restoration of the stack and notification of which exception.
However CATCH doesn't know or care what the stack-diagram of MY-THING is.
So the stack pointer is restored, maybe indeed revealing garbage:
1 2 3 a z \ Throw at this point of 17
^
1 2 3 a z garbage-item 17
^
restored stackpointer.
So that is what is seen at the point of the catch.

So: `` 1 2 3 '' is never touched, not even in case of a throw.
And the `` a z garbage-item '' doesn't contain information and should
be disposed of. Manually that is, because CATCH doesn't know.

(One *could* use it, this is Forth after all, but that would be really
bad practice.)

I find exceptions in Forth easy because you're not tempted to let them
bubble up to a point where you no longer know what to do with them.

>
>Cheers,
>Arnold


1] And `` 1 2 3 d 0 '' if it went okay.

Elizabeth D. Rather

unread,
May 31, 2012, 2:43:34 PM5/31/12
to
On 5/31/12 7:40 AM, Arnold Doray wrote:
...
> : timestamp-jkl ( img -- time )
> ['] grab-timestamp-jkl catch if DROP 0 else ymdhm then ;

A few more words of (possibly gratuitous) advice re CATCH/THROW:

Both systems and applications may use CATCH/THROW for exception
management. Certain THROW codes are documented for potential system use,
although most systems implement only a subset (some are, in fact,
conditions that are virtually impossible to detect). System THROW codes
are all negative numbers, so the universe of positive numbers is
available to application writers.

The general advisory is that an exception handler should completely
process those exceptions it can, and THROW any others up to a higher
level. Thus, if grab-timestamp-jkl has, for example, encountered a
timestamp with missing information (e.g. h m ) you might be able to
perform some remedial action, whereas if a lower-level word encountered
a fundamental error you can't do anything more useful than pass that
THROW code to a higher level that can cope with it appropriately.

All systems that use CATCH/THROW are required to have a high-level CATCH
that can process any THROW codes that aren't given specific processing
in the application. So, a typical sequence in your application might be
something like this:

... ['] grab-timestamp-jkl CATCH ?DUP IF
DUP 95 = IF DROP ... ( my exception #95, my code handles it here )
ELSE ( not my problem) THROW THEN
ELSE ( no exception) ymdhm THEN ...

Often the top-level CATCH will pass a non-zero THROW code into a CASE
statement to issue appropriate error messages. If an application has
multiple exceptions that it wants to handle, it can do the same.

Cheers,
Elizabeth

--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com

"Forth-based products and Services for real-time
applications since 1973."
==================================================

Arnold Doray

unread,
May 31, 2012, 10:56:52 PM5/31/12
to
Thank you, that is useful advice.

In Java this is sometimes done, but all too often the practice is to let
the original caller deal with it, or to fail silently. IMHO probably
because exception generation & handling is clumsier in Java compared to
Forth.

What is nice in Java is the ability to trace the exception's propagation
path (unlike Forth, in Java exceptions are objects that are propagated
down the calling hierarchy). Not too hard to do with Forth say by adding
a new word CATCH* .

Cheers,
Arnold



Arnold Doray

unread,
May 31, 2012, 11:09:06 PM5/31/12
to
On Thu, 31 May 2012 18:38:20 +0000, Albert van der Horst wrote:

<snip>

> I find exceptions in Forth easy because you're not tempted to let them
> bubble up to a point where you no longer know what to do with them.

A very interesting observation.

Java has a much more complicated/formalized system for exceptions (there
are exceptions, runtime exceptions, throwables, etc) but in practice,
exceptions are simply "bubbled up" to the original caller or discarded.
I'm not sure why this is the case. I think partly due to clumsiness, but
there is possibly more to it than that.

Cheers,
Arnold

Andrew Haley

unread,
Jun 1, 2012, 3:44:35 AM6/1/12
to
AFAIK this phenomenon has happened in all languages with exceptions.
Exception handling often seems like a simple way to arrange things,
and it's seductive. It's very easy to forget that exceptions should
be exceptional.

A classic example of overuse in Java is ClassLoader.findClass(), which
throws an exception if it can't find the given class. ClassLoaders
delegate, and failing to find a class is perfectly normal, and no sign
of a problem; you simply try another class loader. So, findClass()
could simply return null. Because of this design, starting up a Java
program often involves tens of thousands of exceptions. I don't think
that it'd be designed that way today, but it's too late now.

Andrew.

Anton Ertl

unread,
Jun 1, 2012, 5:49:20 AM6/1/12
to
"Elizabeth D. Rather" <era...@forth.com> writes:
>All systems that use CATCH/THROW are required to have a high-level CATCH
>that can process any THROW codes that aren't given specific processing
>in the application. So, a typical sequence in your application might be
>something like this:
>
>... ['] grab-timestamp-jkl CATCH ?DUP IF
> DUP 95 = IF DROP ... ( my exception #95, my code handles it here )
> ELSE ( not my problem) THROW THEN
> ELSE ( no exception) ymdhm THEN ...

That's clumsy. Make use of the fact that 0 THROW is a noop:

... ['] grab-timestamp-jkl CATCH dup 95 = if
drop ... ( my exception #95, my code handles it here )
ELSE
throw ( no exception ) ymdhm
THEN
...

- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2012: http://www.euroforth.org/ef12/

Mark Wills

unread,
Jun 1, 2012, 7:57:29 AM6/1/12
to
On Jun 1, 8:44 am, Andrew Haley <andre...@littlepinkcloud.invalid>
wrote:
In .net we have TRY/CATCH/FINALLY which I always thought was very
nice:

Try
Code to try goes here
Catch(ex)
Code to handle exception goes here.
Exception number passed in as ex
Finally
Clean-up code goes here
End Try



Nice.

Anton Ertl

unread,
Jun 1, 2012, 8:45:06 AM6/1/12
to
Mark Wills <markrob...@yahoo.co.uk> writes:
>In .net we have TRY/CATCH/FINALLY which I always thought was very
>nice:
>
>Try
> Code to try goes here
>Catch(ex)
> Code to handle exception goes here.
> Exception number passed in as ex
>Finally
> Clean-up code goes here
>End Try

Gforth has:

| TRY
| code1
| IFERROR
| code2
| THEN
| code3
| ENDTRY
|
|This performs code1. If code1 completes normally, execution
|continues with code3. If there is an exception in code1 or
|before @code{endtry, the stacks are reset to the depth during
|@code{try, the throw value is pushed on the data stack, and execution
|continues at code2, and finally falls through to code3.

code2 is the exception handling code and code3 is the cleanup code.

One advantage over using CATCH is that an exception in the cleanup
code (code3) will make execution containue right after code1. While
this can lead to infinite loops if there is a bug in code2 or code3,
and one has to make sure that the data used by code2 and code3 is
preserved until the ENDTRY, for correct code it ensures that the
cleanup is always performed (while with CATCH-based solutions a Ctrl-C
at the wrong time can produce a nasty surprise.

OTOH, in the years since I implemented this, I have developed a much
higher opinion for xt-based interfaces like CATCH. Maybe we can find
a way to do something like this TRY with an xt-based interface (but
currently I have no idea how).

Andrew Haley

unread,
Jun 1, 2012, 9:45:59 AM6/1/12
to
Mark Wills <markrob...@yahoo.co.uk> wrote:
>
> In .net we have TRY/CATCH/FINALLY which I always thought was very
> nice:
>
> Try
> Code to try goes here
> Catch(ex)
> Code to handle exception goes here.
> Exception number passed in as ex
> Finally
> Clean-up code goes here
> End Try

Yeah. They got that from Java; I don't know when it was invented.
It's not in CLU, which is always my first guess.

Andrew.

Arnold Doray

unread,
Jun 1, 2012, 11:18:29 AM6/1/12
to
On Fri, 01 Jun 2012 02:44:35 -0500, Andrew Haley wrote:

> A classic example of overuse in Java is ClassLoader.findClass(), which
> throws an exception if it can't find the given class. ClassLoaders
> delegate, and failing to find a class is perfectly normal, and no sign
> of a problem; you simply try another class loader. So, findClass()
> could simply return null.

That's a good observation. There seems to be a misplaced desire to be
detailed about exactly what went wrong, with different errors for
separate failure modes. But most app programmers simply

}catch(Exception e){ ... }

and be done with it. The app programmer doesn't care about how failure
occured, only that it did.

And even the same type of error could have come from multiple causes, so
the multiplicity of exception types doesn't really convey much
information even if you did handle them separately. There are exceptions
to the rule (pun intended), but I'd say 90% of Java code using library
code does this.

Cheers,
Arnold









Elizabeth D. Rather

unread,
Jun 1, 2012, 3:18:22 PM6/1/12
to
On 5/31/12 11:49 PM, Anton Ertl wrote:
> "Elizabeth D. Rather"<era...@forth.com> writes:
>> All systems that use CATCH/THROW are required to have a high-level CATCH
>> that can process any THROW codes that aren't given specific processing
>> in the application. So, a typical sequence in your application might be
>> something like this:
>>
>> ... ['] grab-timestamp-jkl CATCH ?DUP IF
>> DUP 95 = IF DROP ... ( my exception #95, my code handles it here )
>> ELSE ( not my problem) THROW THEN
>> ELSE ( no exception) ymdhm THEN ...
>
> That's clumsy. Make use of the fact that 0 THROW is a noop:
>
> ... ['] grab-timestamp-jkl CATCH dup 95 = if
> drop ... ( my exception #95, my code handles it here )
> ELSE
> throw ( no exception ) ymdhm
> THEN
> ...

Yes, that's better.

WJ

unread,
Mar 15, 2013, 2:43:56 AM3/15/13
to
Arnold Doray wrote:

> On Wed, 30 May 2012 02:56:05 -0500, Andrew Haley wrote:
>
> > Arnold Doray <inv...@invalid.com> wrote:
> >> On Tue, 29 May 2012 09:52:34 -0500, Andrew Haley wrote:
> >>
> >>>> Here's a solution using the shortcircuit operators:
> >>>>
> >>>> : | ` if ` true ` exit ` then ; immediate : -> ` not ` if `
> >>>> exit ` then ; immediate
> >>>>
> >>>> : year ( img n1 n2 n3 n4 -- m f )
> >>>> img2int | drop 0 false ;
> >>>>
> >>>> : month ( img n1 n2 n3 n4 -- m f )
> >>>> img2int | drop drop 0 false ;
> >>>>
> >>>> : day ( img n1 n2 n3 n4 -- m f )
> >>>> img2int | drop drop drop 0 false ;
> >>>>
> >>>> : hour ( img n1 n2 n3 n4 -- m f )
> >>>> img2int | drop drop drop drop 0 false ;
> >>>>
> >>>> : minute ( img n1 n2 n3 n4 -- m f )
> >>>> img2int | drop drop drop drop drop 0 false ;
> >>>
> >>> What's going on here? MINUTE takes an image, converts it to a number,
> >>> tests if it's OK, then drops a lot of stuff. Where did that stuff you
> >>> just dropped come from?
> >>
> >> If IMG2INT in MINUTE fails, MINUTE needs to clear the integers put on
> >> the stack by YEAR MONTH etc. If IMG2INT succeeds, the short-circuiting
> >> operator | returns to the caller.
> >
> > But that's terrible factoring: it means that MINUTE can oly be used in
> > that context! What sense does that make?
> >
>
> Not so "horrible" because MINUTE is reused in the same context again and
> again with different image types. And MINUTE only makes sense when used
> in conjunction with YEAR, etc. And finally, they are "private" words that
> are forgotten outside this module.
>
> But it's not ideal. As others have pointed out, using THROW/CATCH is
> probably better:
>
> : exit! if true throw then ;
> : & img2int not exit! ;
>
> : grab-timestamp-jkl { img } ( img -- y m d h m | error )
> img 170 13 245 40 &
> img 245 13 289 40 &
> img 289 13 331 40 &
> img 331 13 372 40 &
> img 381 13 420 40 & ;
>
> : timestamp-jkl ( img -- time )
> ['] grab-timestamp-jkl catch if 0 else ymdhm then ;
>
> But there is the inconvenience of having to use 2 words instead of one.
>

Factor:

When img2int succeeds, it puts ( int t ) on the stack.
When it fails, ( f f ).

USING: locals ;

:: grab-timestamp-jkl ( img -- vec/f )
V{ } :> vec
{ { 170 13 245 40 }
{ 245 13 289 40 }
{ 289 13 331 40 }
{ 331 13 372 40 }
{ 381 13 420 40 } }
[ img img2int [ vec push t ] when ]
all? vec f ?
;


0 new messages