4

When DO is followed by a function, that function is executed and the remaining values are consumed as arguments according to the arity of the given function, e.g.,

do :multiply 3 4
multiply 3 4

These two statements are identical in their effects. But I think DO + function receives special treatment by the REBOL interpreter, because I don't believe it's possible to implement your own DO (with the exact same syntax) in pure REBOL, e.g.,

perform: func [f [any-function!]] [
    ; What goes here?
]

Is this correct?

Clarification

I am not asking about the DO dialect. This is not a "beginner" question. I understand REBOL's general syntax very, very well: Bindology (an old blog post I did on it), the implications of its homoiconicity, the various flavors of words, and all the rest. (For example, here is my implementation of Logo's cascade in REBOL. While I'm at it, why not plug my Vim syntax plug-in for REBOL.)

I'm asking something more subtle. I'm not sure how I can phrase it more clearly than I already have, so I'll ask you to read my original question more carefully. I want to achieve a function that, like DO, has the following capability:

do :multiply 3 4
double: func [n] [n * 2]
do :double 5

Notice how the syntax do :double or do :multiply consumes the appropriate number of REBOL values after it. This is the key to understanding what I'm asking. As far as I can tell, it is not possible to write your own REBOL function that can DO this.

You'll have answered this question when you can write your own function in pure REBOL that can be substituted for DO in the examples above—without dialects, blocks, or any other modifications—or explain why it can't be done.

Community
  • 1
  • 1
Gregory Higley
  • 15,923
  • 9
  • 67
  • 96
  • Brett has a nice description of set-words, get-words and lit-words here with good examples http://www.codeconscious.com/rebol/articles/rebol-concepts.html – johnk Dec 24 '14 at 11:29
  • 2
    Notice my desperate desire not to be mistaken for a beginner. I need a therapist. :) – Gregory Higley Dec 24 '14 at 14:58

3 Answers3

2

The cause of the behavior you are seeing is specifically this line of code for the Rebol native DO.

/***********************************************************************
**
*/  REBNATIVE(do)
/*
***********************************************************************/
{
    REBVAL *value = D_ARG(1);

    switch (VAL_TYPE(value)) {

       /* ... */

    case REB_NATIVE:
    case REB_ACTION:
    case REB_COMMAND:
    case REB_REBCODE:
    case REB_OP:
    case REB_CLOSURE:
    case REB_FUNCTION:
        VAL_SET_OPT(value, OPTS_REVAL); /* <-- that */
        return R_ARG1;

This OPTS_REVAL can be found in sys-value.h, where you'll find some other special control bits...like the hidden "line break" flag:

// Value option flags:
enum {
    OPTS_LINE = 0,  // Line break occurs before this value
    OPTS_LOCK,      // Lock word from modification
    OPTS_REVAL,     // Reevaluate result value
    OPTS_UNWORD,    // Not a normal word
    OPTS_TEMP,      // Temporary flag - variety of uses
    OPTS_HIDE,      // Hide the word
};

So the way the DO native handles a function is to return a kind of "activated" function value. But you cannot make your own values with this flag set in user code. The only place in the entire codebase that sets the flag is this snippet in the DO native.

It looks like something that could be given the axe, as APPLY does this more cleanly and within the definitions of the system.

  • 1
    This is what I suspected. I should have just gone and looked at the source. I've spelunked it before. – Gregory Higley Dec 24 '14 at 15:16
  • I'd like to see it removed for consistency or (as an experiment) expanded, e.g., `func perform [[activated] f [any-function!]] [:f]`, where the `activated` attribute tells the interpreter what to do. I can see lots of interesting syntactic possibilities with this, but I also see lots of problems. (Primarily code that is very hard to understand. Homoiconicity is abstract enough without adding something like this. Still, it'd be fascinating to see what could be done.) – Gregory Higley Dec 24 '14 at 16:19
2

Yes, in Rebol 3:

>> perform: func [f [any-function!]] [return/redo :f]
>> perform :multiply 3 4
== 12

>> double: func [n] [n * 2]
>> perform :double 5
== 10

You might find it interesting to read: Why does return/redo evaluate result functions in the calling context, but block results are not evaluated?

Community
  • 1
  • 1
Brett
  • 428
  • 3
  • 11
  • 2
    Good to point out for historical purposes although the refinement has been removed from current versions due to problems with it. See [CC #2121](http://curecode.org/rebol3/ticket.rsp?id=2121) – HostileFork says dont trust SE Dec 25 '14 at 00:57
0

This is a good question, and I will try to explain it to the best of my understanding. The two statements above are identical in effect, but it is worth diving deeper into what is happening.

The :word syntax is known as a get-word! and is equivalent to writing get 'word. So another way of writing this would be

do get 'multiply 3 4

multiply is just another word! to Rebol.

The do dialect is the default dialect used by the Rebol interpreter.

If you want to implement your own version of do you need to be evaluating your code/data yourself, not using do. Here is a trivial example:

perform: func [ code [block!]] [ if equal? code [ 1 ] [ print "Hello" ] ]

This defines perform as a function which takes a block of code. The "language" or dialect it is expecting is trivial in that the syntax is just perform an action (print "hello") if the code passed is the integer 1.

If this was called as perform [ multiply 3 4 ] nothing would happen as code is not equal to 1. The only way it would do something is if it was passed a block! containing 1.

>> perform [ 1 ]    
Hello

Expanding on this slightly:

perform: func [ code [block!]] [ if equal? code [ multiply 3 4 ] [ 42 ] ]

would give us a perform which behaves very differently.

>> perform [ multiply 3 4 ]
== 42

You can easily write your own do to evaluate your dialect, but if you run it directly then you are already running within the do dialect so you need to call a function of some kind to bootstrap your own dialect.

This jumping between dialects is a normal way to write Rebol code, a good example of this being the parse dialect

parse [ 1 2.4 3 ] [ some number! ]

which has it's own syntax and even reuses existing do dialect words such as skip but with a different meaning.

johnk
  • 1,102
  • 7
  • 12
  • I'm not sure that I answered all of your question regarding the exact syntax, but please come and visit our chat room and hopefully we can explain http://chat.stackoverflow.com/rooms/291/rebol-and-red – johnk Dec 24 '14 at 11:25
  • While this is a _great_ explanation of REBOL concepts for a beginner, I'm far from that. (For example, [here](http://stackoverflow.com/a/27591674/27779) is my implementation in REBOL of Logo's `cascade` function.) Unfortunately you've completely missed the point of my question, which is a bit more subtle than you've realized. – Gregory Higley Dec 24 '14 at 13:26
  • I wish, @johnk, that we could move your excellent answer to another question to which it applies more closely. It explains many things a beginner might want to know, but as you can see, it's not the answer to what I was asking. – Gregory Higley Dec 24 '14 at 15:33
  • 1
    I'll be sure to read the question more carefully next time! Cheers – johnk Dec 24 '14 at 20:51