I'm bringin' EVERY back (yeah!)


#1

(:man_dancing: :musical_note: :desktop_computer: …them other boys don’t know how to hack (yeah!) :musical_note: :man_dancing: :computer:)

With apologies to Justin Timberlake, I re-present to you EVERY. It was temporarily shelved due to ambiguous semantics, that recent changes to the loop result protocol have basically solved.

It’s like a FOR-EACH but if the loop body ever returns false, it returns false:

>> every x [1 3 7] [print [x] | odd? x]
1
3
7
== #[true]

>> every x [1 4 7] [print [x] | odd? x]
1
4
7
== #[false]

So EVERY runs through all the loop steps (assuming you don’t break), but remembers if the body ever returns a falsey value…and if so returns #[false]. Otherwise it returns the last truthy value.

Note that CONTINUE is interpreted as CONTINUE NULL, which means “act as if the loop body reached the end and had evaluated to null for this iteration”. As null is falsey, that means you need to say CONTINUE TRUE (or other truthy value) if you want that loop iteration to not count as disrupting the everyness.

>> every x [1 2 3] [print [x] | if x = 2 [continue] x]
1
2
3
== #[false]

>> every x [1 2 3] [print [x] | if x = 2 [continue true] x]
1
2
3
== 3

It still obeys the loop result protocol, so that means if the loop ever breaks you’ll get a NULL… and if the loop body never runs you’ll get blank. All three states are falsey so you get:

every x data [
    ...
] or [
    // code to run if there was a break, there was no data,
    // or any of the loop body runs returned null/break/false
]

If you’re trying to build an opt-out situation which would default to an empty input being okay (instead of falsey like if the body failed), test against false explicitly so the blank case won’t qualify:

false <> every x data [
    // if this body returns false (null, blank) then that will
    // make the whole expression false.  But the no data
    // case which returns blank will still give you truthyness
]

Though just because of how things work out, that will treat BREAK as a “success”, since false <> null. So if you have a need to use both BREAKs and falsey loop bodies in the every, turn the nulls into false explicitly:

false <> (every x data [
    ...
] else [false])

Loop iterations must return something that can be tested for conditional truth or falsehood, so voids aren’t legal. This is an important distinction with FOR-EACH, which is able to run loop bodies that return void

>> every x [1 2 3] [print "this errors, void loop body result"]
this errors, void loop body result
** Script Error: VOID! values are not conditionally true or false

:musical_note: …so get your EVERY on (go 'head, be gone with it) :musical_note: