Taking ACTION! on "Function vs. Action"

The very noun-sounding FUNCTION is an active generator, whose word sounds like it should be the name for a variable holding a type of its value:

block: [This is a block!]
string: {This is a string!}
function: :append ;-- this is a FUNCTION!, but...
                  ;-- ...now I can't use FUNCTION to declare another!

Some have said users would have a better mental model of the generation process if it were MAKE FUNCTION! (or MAKE-FUNCTION, or FUNCTIONALIZE, or FUNCTIONIFY, or [x [integer!]] => [x + 1]), or ....

But plain old FUNCTION being a generator is rather entrenched, and there's problems with pretty much any suggestion for changing it. While things like => are good for lambdas, that's not as readable as prefix for top level declarations. The only one I could see liking would be make function! but that opens huge cans of worms.

What could change, easily, is the datatype name. Because Ren-C made the important change known-up-to-now as "OneFunction", it freed up all the words for the old subclasses (NATIVE!, ACTION!, ROUTINE!, OP!). So...

What if the "OneFunction" FUNCTION! were just called ACTION! instead?

I already approximate this with action [function!] on many parameters, just to avoid tripping over the name function, or using an ugly abbreviation like f, and func is taken too (so even fun feels sketchy).

One can argue that since FUNCTION! doesn't guarantee it acts like a "mathematical function" anyway, that the less-loaded term could be better. It is more comprehensible, I think, to say "FUNCTION is an ACTION!, whose purpose is to generate another ACTION!..." It's a shorter word. It still has "tion" in it. :slight_smile:

Backwards compatibility wouldn't be that hard, as that's just function!: :action!. To the casual user, the difference would be that all the native interfaces would call generic function parameters action [action!] (unless it were something like comparator [action!])...and type of would visibly tell them they had an ACTION!.

Seeing an apply-able value come back as ACTION! would be no big shock to Rebol2 or Red users. Their main confusion was probably before things like "hey, why did I say my argument could be a FUNCTION! but I can't pass APPEND to it..." Now it would just be "well, everything is an action!".

It's not a huge change, and it's been on the table for a while now. But no new information has come to light since the idea first appeared, around the time of OneFunction. We know more or less everything we are going to know about this.

What do people think? Is saying Rebol runs ACTIONS in CONTEXTS good, or bad?

The biggest area this probably affects is the interpreter source itself, where there's tons of references to fun or VAL_FUNC or FUNC_NUM_PARAMS. I've held off on some updates to those, which fitting patterns of other types these days would look more like:

REBCTX *ctx = VAL_CONTEXT(...); // full word used for main extractor
int n = CTX_NUM_VARS(ctx); // abbreviation used for sub-extractions

REBFUN *fun = VAL_FUNCTION(...); // ...so this should match up
int n = FUN_NUM_PARAMS(fun); // ...and so should this

FWIW: My main reluctance on this comes from feeling like the interpreter source wouldn't read as well:

REBACT *act = VAL_ACTION(...);
int n = ACT_NUM_PARAMS(act);

Though historically, such things start seeming normal pretty quickly. For instance: I thought CTX was too "ugly" an abbreviation compared to REBCON, but I now think it's "obviously better".

It would also help distinguish between C functions (CFUNC) and Rebol actions...which would add value.

Or am I wrong, is there still some un-thought-of suggestion or issue to be discussed here? Can we commit to a decision now, or not!?

Note that Red/Rebol2/R3-Alpha say the following:

>> function? :append
== false ;-- wait, what?!

>> type? :append
== action! ;-- er... that's not exactly... clarifying

Making action? work for all Rebol-invokable-things has the advantages I list. Quick re-summary:

  • Has existing precedent as a word used to describe invokable things.
  • Shorter name than FUNCTION!
  • Helps disambiguate in the interpreter source itself, that if a variable holds a "function" you're talking about C executable code, and if you're holding an "action" you're talking about Rebol executable code.
  • "Rebol: Actions in Contexts" sounds differentiating...as opposed to "Rebol: Functions in Objects" which does not really command much attention.
  • Less loaded term than function, which has a more strict mathematical meaning, regarding consistency of same output for same inputs...no side effects...etc.

And of course, the reason that motivated it all: you can call a variable action to hold an action! without destroying the ability to make new ones, the way making a variable function to hold a function! would.

So I'm going to change "OneFunction" to be OneAction. The list of PROs is long, there aren't many cons (considering that historically was (function? :append) = false, it can't be considered a "step backwards" in any way). I've long known it's better than FUNCTION!, my only worry has been that some other idea or paradigm is even better, which has made me hold off. But waiting longer has ceased to make sense.

After four years of relative peace in naming, there's a new issue...

Whatever we call the "thing-that-is-now-ACTION!", it will come in two forms...

The non-quoted form will not execute without an apply. Here is an example of a function whose parameter takes just a plain action!, and so you can run it.

 foo: func [act [action!]] [
     if action? act [
         print "This will work even though you didn't say :act"
    apply act ["one" #way 2 <run>]  ; again, no :act needed
    act/ ["another" #way 2 <run>]

The isotopic form will run when referenced through a WORD! As with history, you'll need to disarm it somehow or another to get at it:

>> :append
== ~#[action! [series value /dup /part]]~  ; isotope

 >> ^append
 == ~#[action! series value /dup /part]]~

>> append [a b c] [d e]
== [a b c [d e]]

How Might This Shuffle Naming?

An isotopic group is called a "splice".

An isotopic block is called a "pack".

In the prototype implementation, I'm calling isotopic actions "activations". But that's a little weird.

An alternate conception might be that the inert thing is a FUNCTION! and once it's isotopic it would be called an ACTION!.

Or we just call them "action isotopes". Maybe that's fine, in the sense that there's not as much of a difference in semantics to convey as with packs and splices.



function / action
(inert) action / (active) action
or action and action isotope are all fine with me.