Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVERY loop construct semantics unclear #847

Closed
hostilefork opened this issue Aug 27, 2018 · 1 comment
Closed

EVERY loop construct semantics unclear #847

hostilefork opened this issue Aug 27, 2018 · 1 comment

Comments

@hostilefork
Copy link
Member

hostilefork commented Aug 27, 2018

EVERY is a loop construct which was introduced to address those who might have a naming objection to FOR-EACH being hyphenated. The proposal was that it would be like FOR-EACH but have a different return result...responding if every iteration of the loop is truthy...and if so, returning the last truthy result, sort of like a non-short-circuit ALL.

Yet there have been no usages and no tests written, and there are open semantic questions. For instance, as a loop construct it is supposed to be reserving NULL for BREAK... which is contentious with acting like ALL and using NULL as the differentiating result for when no value is found.

It's not a hard construct to write if it were understood what it should do. But in the interests of not dragging down other development for something no one has used yet, it is being cut from Beta/One. Here was what was written on the Trello Card about it previously:

Precedent in underscore.js had every as a logic test for applying functions over an array, meaning "if every evaluation is true".

In the Rebol world, this suggests acting like FOR-EACH but having a different return result than the loop-standard "last item evaluated in the body". Instead, it will return the last value if all are conditionally true, but will return NONE if any of the values were conditionally false through the evaluation.

>> every x [1 3 5] [odd? x]
== true ;-- all TRUE?, last item

>> every x [1 3 5] [x]
== 5 ;-- all TRUE?, last evaluation

>> every x [false 3 5] [x]
== _ ;-- not all TRUE? so BLANK!

Where FOR-EACH (generally) returns the last body evaluation, like WHILE and UNTIL, this behaves more like ALL MAP-EACH.

Historically the return result of FOR-EACH is rarely used. Turning to EVERY as the first enumerator to teach is a good thing. The return result of EVERY is actually useful, while the result of FOR-EACH almost never is. And EVERY is a shorter name and unhyphenated.

Here was the native spec:

//
//  every: native [
//
//  {Returns last TRUE? value if evaluating a block over a series is all TRUE?}
//
//      return: [<opt> any-value!]
//          {TRUE or BLANK! collected, or BREAK value, TRUE if never run.}
//      'vars [word! block!]
//          "Word or block of words to set each time (local)"
//      data [any-series! any-context! map! blank! datatype!]
//          "The series to traverse"
//      body [block!]
//          "Block to evaluate each time"
//  ]
//

A further comment from the Trello on naming:

Though the native was renamed to FOR-EACH, it's still in the compatibility module with other things. But unlike cases such as TYPE? or LENGTH?, there's not a "conceptual problem"...and nothing is clamoring to retake FOREACH for anything else. It may be kept around as a synonym and left as a matter of taste.

But do consider that while foreach may have been comfortable for some as a word, its hard not to see the word reach inside of it. Once you recognize that it's not a word and see it with fresh eyes, it looks bad...and also not part of the family of other -each functions like remove-each and map-each.

The need for the hyphen for for-each isn't that bad, but the hyphen does break the rhythm a little bit. every was selected as a near-synonym of for-each (with a different return result).

hostilefork added a commit that referenced this issue Aug 27, 2018
Originally, the idea that each frame needed one cell's worth of
allocation came out of the need to be able to have a place to put a
generated value value during an EVAL operation.  Not only did it need
a location to be written into, but it also had to be GC protected:

    eval (func [] [recycle | 'print]) "What keeps func alive?"

Eventually, designing Eval_Core() to be re-entered allowed eval to
keep its argument alive by leaving it exactly where it was...in the
argument slot of the frame of the EVAL call.  Yet in the meantime,
the frame's cell had been exported as "D_CELL"...to be visible as a way
for native actions to get a scratch cell "for free" that was GC safe.

But unlike EVAL, a non-negotiable need for a non-movable per-frame
cell of storage space arose with the SHOVE operation.  The right hand
side of the operator can only be evaluated once, and its product must
be able to outlive evaluations on the left hand side.  Putting it on
the data stack is not an option, as it would be movable and the
f->gotten pointer could not stay fixed to it...not to mention needing
to adjust the data stack pointer to account for it during another
evaluation.

This removes D_CELL, and takes away the idea that natives are allowed
access to the frame's cell...reserving it for the evaluator's internal
usage.  Instead, it uses locals pushed with PUSH_GC_GUARD().

Includes improvements to catch unbalanced GC guards correctly, as the
feature of checking it had atrophied from lack of use.

Also removes the EVERY operation, as it was a client of the D_CELL that
had become broken.  It would involve thinking to fix it, and removing
it entirely seems better than leaving it broken.  See:

#847
@hostilefork
Copy link
Member Author

hostilefork commented Nov 19, 2018

This has essentially been resolved by the loop result protocol's increased willingness to mutate loop results (e.g. blanks to become void, to distinguish them from the case of a loop body never running).

In EVERY's case, the protocol "falsifies" null and blank...which means it's still able to use blank for the loop body never getting a chance to run, and NULL for the loop breaking. Since all three are falsey, this presents a particularly interesting "we had content, and it passed" default condition.

In any case, the feature has been restored:

1fefdbf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant