Rebol's Target Market: Newbies, Experts, or Other?

As @iArnold decided to ask to build Ren-C and is messing with it, I do feel a mixture of defensiveness and depressedness about how while many things are going well, it's a long way from "usable" by laypeople (lots of open questions and half-finished experiments...)

But it's important to remember why I have gone down this alternate focus path; and to point out what Ren-C can do.

So let's look back at DocKimbel's own words circa 2008:


I said that in that thread his example is the most interesting. But it doesn't work in Rebol2/Red due to binding problems.

So let's say you have a file %facts.red (no header, because Red's LOAD does not skip the header):

; %facts.red

[a > 3]  "a greater than 3"
[b < 10] "b lesser than 10"

I'll start by being charitable and modifying his example to not use RETURN and hence not be in a function, just print msg and not return msg

Red [file: %charitable.red]

a: 10
b: 20

foreach [condition msg] load %facts.red [
    if do condition [print msg]
]

And that works:

>> do %charitable.red
a greater than 3

But now let's go back to his version, where we use RETURN and hence are inside of a function:

Red [file: %uncharitable.red]

checker: func [a b] [
    foreach [condition msg] load %facts.red [
        if do condition [return msg]
    ]
]

probe checker 10 20

When we run it:

>> do %uncharitable.red
*** Script Error: a has no value

The LOAD of %facts.red did a deep walk and bind of the blocks as "scoped" inside of %facts.red, not scoped in the context where the code is being loaded... the body of the checker function.

This means the notion of a, and b aren't available (and if you've overridden > and < to something besides what LOAD used globally, those aren't available either).

What DocKimbel was advising you do was not a substitution you could write without a long explanation of caveats. If you don't have a generalized answer then that is a major indictment of the "oh, you'd just do this" hand-waving that is so endemic in Redbol culture, a pattern that has led to overpromises and underdelivery for decades. :pouting_cat:

In Ren-C, There Are Ways Of Doing This

As I've explained in Ren-C Binding In A Nutshell, controlling binding depends on being able to start from a ground level of "not bound" at loading time...yet have that not be a death sentence to using your code.

So our "facts" can come in as unbound, and then we can get our bindings from various sources...even inherit them from compounded envrionments like you would find in a function frame.

There's lots of degrees of freedom...and understanding how exactly to go about this depends on why you were doing the separation.

For starters, let's imagine that you want to splice the facts in just once. You can do that with a COMPOSE... but let's take note of the details! (Remember that BIND1 is going to be just plain BIND... or you could use $)

Rebol [file: %compose-once.r]

checker: func [a b] (compose [  
    for-each [condition msg] bind1 (transcode read %facts.r) [
        if eval condition [return msg]
    ]
])

probe checker 10 20

When we do this:

>> do %compose-once.r
"a greater than 3"
== \~okay~\  ; antiform (logic!)

Note that I used transcode read instead of LOAD. This is because my conceptual understanding of what LOAD is is that it be sensitive to some kind of "knowledge" of what it's loading... giving you material that is bound "the way you would expect". So if this were some kind of "fact language" then maybe LOAD would know enough to bind it in such a way that it knew what a and b and > and < meant, and gave you the right environment.

But because "I don't really know what LOAD is", I'm punting on the question of how you ask it to give you unbound code. transcode read has a clear definition and is uninvolved with binding. It might be that if you pass transcode a FILE! it should treat that as transcode read, but that carries the weight of saying that transcode x for an unknown X might do file I/O... and I'm not sure if that polymorphism is worth it. But maybe the value of a "streaming transcode" dictates that it should. :thinking:

Looking more closely at the setup, note that the BIND1 is outside the composed facts:

for-each [condition msg] bind1 (transcode read %facts.r) [...]

...and not:

for-each [condition msg] (bind1 transcode read %facts.r) [...]

This is because the function FRAME! containing a and b does not exist at the time of the COMPOSE, which is at function creation time. If we put the BIND inside the COMPOSE'd group, then we'd be bound to whatever ideas of a and b were visible to the COMPOSE.

And because you said [condition msg] for the loop variables and not ['condition 'msg] it defaults to honoring binding propagation from the "tip" of the thing you're enumerating to the elements retrieved out. So even though the [a > b] inside the transcoded block is itself unbound, it gets a binding when it is written into CONDITION.

What If The Facts Changed Each Call?

That's simpler. You don't need the COMPOSE, just READ the file each time:

Rebol [file: %no-compose.r]

checker: func [a b] [  
    for-each [condition msg] bind1 (transcode read %facts.r) [
        if eval condition [return msg]
    ]
]

write %facts.r --[
    [a > 3]  "a greater than 3"
    [b < 10] "b lesser than 10"
]--
probe checker 10 20

write %facts.r --[
    [a < 3]  "a lesser than 3"
    [b > 10] "b greater than 10"
]--
probe checker 10 20

And that works:

>> do %no-compose.r
"a greater than 3"
"b greater than 10"
== \~okay~\  ; antiform (logic!)

Back To The Original Topic: Newbies, Experts, Other?

When these questions were originally being asked, the concept of coding with modern AI as it is being experienced today was far off the radar.

Today it's pretty clear that there's little point in targeting tools for people to do "simple tasks"--who themselves aren't really interested in the topic of programming.

There will only be self-chosen experts...people who don't trust AI and want to "Rebel" and have code they can read and control...people who want a codebase whose source is simple and trustable where they can read every line at human scale, including the interpreter itself.

Is this avenue of attack still something that can have meaning for that audience?

:man_shrugging:

I don't know. But I know that if it doesn't have a working binding model that can do at least this example... then I don't care about it.