Tunnelling BREAK and CONTINUE Across Binding

In %make-reb-lib.r, there's are abstractions for helping you do something for every API definition. The APIs are in an array of objects, and the idea is to go through all the objects and run some code bound into each one... so you don't have to specify the object name each time.

So instead of:

names: map-each api api-objects [
    print ["The API name is" api.name]
    api.name
]

You could instead write:

names: map-each-api [
    print ["The API name is" name]
    name
]

Implementation of MAP-EACH-API

In order to make the API member fields visible to code, we use the OVERBIND instruction, which patches in the object:

map-each-api: lambda [code [block!]] [
    map-each api api-objects compose [
        eval overbind api (code)
    ]
]

Problem: CONTINUE and BREAK

We'd like to be able to run the CONTINUE and BREAK of the MAP-EACH. Previously, using a COMPOSE bought that. Now, because the code block has a binding, those do not override.

If the API's object's bindings were on the block passed to MAP-EACH, then it would patch the BREAK and CONTINUE onto them. But we can't do that, because the API object is not available until the MAP-EACH is already running and the block has been passed.

Using the current primitive tools for binding at hand, we can try making an object carrying BREAK and CONTINUE and overbind into that as well:

map-each-api: lambda [code [block!]] [
    map-each api api-objects compose [
        let aux: make object! compose [break: (^break) continue: (^continue)]
        eval overbind aux overbind api (code)
    ]
]

It works...and the COMPOSE isn't really necessary anymore. But, it certainly leaves a lot to be desired. If we were to dialect this (in the spirit of something like hiiamboris's WITH), it might look more like:

map-each-api: lambda [code [block!]] [
    map-each api api-objects [
        eval with [break continue :api] code
    ]
]

Overall though, it feels good to have things under more strict control.

1 Like