On changing rebRun() to rebValue()


In the early development of the libRebol API, there was a reason that the name rebRun() was chosen over rebDo(). Otherwise, you might think that rebDo("%script.reb") would return the result of executing the script, not the FILE! value %script.reb

Hence it was more like do [%script.reb] than it was like do %script.reb. The ambiguity suggested the use of another word. rebRun() was picked for lack of a better idea.

But that was just the first routine that ran code. The API evolved to have many more, where the idea was that having entry points that returned narrower types made things simpler. Not going through a moment of ever returning a REBVAL* API handle for the evaluated product saved the expense of making that handle, and the client's hassle of releasing it with rebRelease().

REBVAL *value = rebRun("first", block);  // pays cost of API handle allocation
...use value...
rebRelease(value);  // client must do this (though fail() triggers auto-release)

int i = rebUnboxInteger("1 +", some_number);  // no handle cost, no release hassle!

if (rebDidQ("find [a b c]", word)) { ... }  // no handle cost, no release hassle!
if (rebNot("even?", some_number) { ... }  // no handle cost, no release hassle!

rebElide("print {No result, and introduces the ELIDE language feature}");

BUT it's pretty easy to forget rebRun() returns a value...

I know the issues better than anyone will, for the near future. Even so, I'll slip up and say something like:

rebRun("print {Whatever}");  // oops, API handle dropped on floor, leak!

There's nothing about "running" that directly reminds you a value is going to be returned, nor that reinforces you'd have any particular responsibility for rebRelease()'ing it.

So what about rebValue()?

It might seem a bit strange, but it works. And when you look at things like the need to combine promises with the other words, reb.PromiseValue() or comes off more coherently than reb.PromiseRun().

What would you think the argument to rebValue() should be, other than code which produces a value?

rebValue("print {Missing assignment seems wrong, doesn't it?}")

REBVAL *v = rebValue("second [that's {better} #isn't <it?>]");

The only thing I can think of is that in your language, you might think it's going to take your data type literally. So rebValue(1) would be an INTEGER! of 1, and rebValue("hello") would be a TEXT! containing hello. The latter is not going to happen as text literals are interpreted as code...that's fundamental (unless your language has more than one string literal type). But the former might still work, as would rebDid("integer?", 10) in non-C languages with fancier type systems. C++ can certainly do that.

Potential conflicts with REBVAL names in various languages

While REBVAL is what's being used in the API now for the datatype itself, it's far from final. Red chose red_value as lowercase for the datatype--and that isn't an uncommon convention in C. (They hid the pointer, so it's red_value v and not red_value *v, which has various implications.)

So there could be some contention when you have a namespace like reb or rebol, and functions in it, that Value could be wanted for the datatype. So in hypothetical language X:

let rebol:Value_Type $varname = (rebol:Value{"1 + 1"})

It could be clumsy. But it's very speculative to worry about these hypothetical situations...I have a feeling that there are going to be more than enough good answers in the languages we know about. And the higher level languages are likely to have even more options at their disposal in their spin of the API.

For instance, in C++ it could be both the datatype and the function name that builds it, because of constructors...so rebol::Value can do double duty:

 rebol::Value sum ("1 +", other_value);