Comments on: Extending EXTEND
REBOL Technologies

Comments on: Extending EXTEND

Carl Sassenrath, CTO
REBOL Technologies
24-Sep-2010 3:08 GMT

Article #0488
Main page || Index || Prior Article [0487] || Next Article [0489] || 10 Comments || Send feedback

For a few of you gurus...

Today, while redesigning the boot sequence for R3, I wanted to make the runtime library system/contexts/lib but without the "noise" of local words in the context.

As you know, when you load a block of code (or data) new bindings are created for all words. This is done to avoid the problem of forward references. Early on in the development of REBOL, we decided that this method was usually worth the "cost" of local words appearing in the target context.

Of course, that behavior is not a strict requirement. If you happen to know all defined words, or can somehow detect them, then you don't need to allocate slots for any other words. In fact, that's how objects are made. They are designed such that their set-words are definitive. All other words must be from higher or lower level contexts.

Now, we haven't really codified this method as an official function. Sure, most of you might know how to use bind to get the desired result, but it seems to me it would be worthwhile to provide it in the system. While most programmers won't care, gurus will find it quite useful, especially when building precise run-time contexts. (Like what I'm doing right now for the boot code.)

When using this method to add to an existing context, it certainly seems like this feature belongs in extend (mezzanine function.) Two forms could be supported:

extend/with acontext words-block eval-block

This provides the list of words to append to the context, followed by a block that is evaluated.

And:

extend/with acontext 'set eval-block

which acts uses the set-words of the eval-block as the word, just like make of objects.

Yes, I know, we've probably all written something like this before for our own apps, but it's time to nail it down and add it as an official feature.

Let me know your thoughts.

10 Comments

Comments:

Brian Hawley
25-Sep-2010 0:27:47
OK, so this adds an option to extend that makes it treat its three arguments differently. This would add the block! type to the 'word argument, and likely put an internal type constraint on the 'val argument when extend/with is called. Some questions come to mind:
  • Will there be a meaning to extend object! block! block!? At the moment I am assuming not, but it could be like set bind/copy/new/only.
  • Will the eval-block argument be bound or bind/copy'ed?
  • Should we make explicit internal type tests with assert/type, or can we count on the type incompatibility of the internal calls to to-set-word, collect-words and bind to do the work for us?

Depending on the answers to these questions, we can do this right away.

Brian Hawley
25-Sep-2010 1:06:32
Here is a version with some of those questions answered:
extend: func [
    "Extend an object, map, or block type with word and value pair."
    obj [object! map! block! paren!]
    word [any-word! block!]
    val
    /with
][
    case [
        block? word [
            word: bind/new collect-words word obj
            either with [set word :val] [do bind/copy :val obj]
        ]
        not with [
            if :val [append obj reduce [to-set-word word :val]]
            :val
        ]
        word = 'set [
            bind/new collect-words/set :val obj
            do bind/copy :val obj
        ]
        'else [
            cause-error 'script 'expect-arg
                compose [expect/with word (:word)]
        ]
    ]
]

This has a meaning for extend object! block! block!, bind-copies eval-block, and uses the type constraints on internal calls to trigger the appropriate type errors, while still flagging anything other than 'set as a error for extend/with. It could use some comments and doc strings though. Is this what you had in mind?

Carl Sassenrath
25-Sep-2010 10:40:08
Ah, very good... And, that reminds me of a few related issues that need to be finished up that might even simplify that a bit more. (I'll reply in AltME.)
Brian Hawley
25-Sep-2010 13:04:59
Based on the AltMe-delivered concerns (and other bug fixes), here is a new version, with docs this time:
extend: func [
    "Extend an object, map, or block type with word and value pair."
    obj [object! map! block! paren!] "Only object! for extend/with"
    word [any-word! block!] "Word(s) to add, or extend/with 'set"
    val "Value(s) to assign or block to do"
    /with "Do val block instead of assigning it to word"
][
    case [
        block? word [ ; Extend obj with words in block
            ; Add the words in the block to the object
            word: bind/copy/only/new word obj  ; must be object!
            ; Either do val block or assign the words
            either with [do bind/copy :val obj] [set word :val]
        ]
        not with [ ; Extend obj with set-word if val
            if :val [append obj reduce [to-set-word word :val]]
            :val
        ]
        word = 'set [ ; Extend obj with set-words in val
            ;assert/type [obj object! val [block! any-word!]
            bind/copy/only/new/set :val obj
            do bind/copy :val obj
        ]
        'else [
            cause-error 'script 'expect-arg
                compose [extend/with word (:word)]
        ]
    ] ; returns val or result of val block
]

What's with the if :val [...] guard? Do we need that? Should we add it elsewhere in the function?

Carl Sassenrath
26-Sep-2010 11:29:01
The :val guard is an appendage from the early R3 GUI days, where extend was born.

But, now that extend has become generally useful, we should remove that exception, which is likely to become trouble.

Gregg Irwin
26-Sep-2010 17:26:29
I'm not clear on the scenarios that EXTEND is intended for, since it has very specific behavior (e.g. casting the word to a set-word), and now the new functionality. The 'set arg doesn't have a great aesthetic for me either.

I found an old blog entry on the subject: http://www.rebol.com/cgi-bin/blog.r?view=0233

It only mentions objects, which comes back to my initial point about its intended purpose. Extending objects is *great*, but what is the goal for the other datatypes?

Gregg Irwin
26-Sep-2010 17:57:25
I'm not using R3 for production work yet, and haven't spent time looking at new feature interactions and the idioms they'll support.

I just looked at SET, and was reminded of the /pad refinement. It's another case where it looks like the object behavior is different. With blocks, if there aren't enough values, the remaining words are set to none, but you have to use /pad to get that behavior with objects. I understand the reasoning behind having the default be "safe" by not overwriting all other values, but wonder if the reverse behavior might be more natural.

-pekr-
27-Sep-2010 10:28:23
The 'set part seems really weird. It feels like what we wanted was /set refinement (we are refining the function behaviour), but then we don't have variable number of parameters to functions ;-)
Brian Hawley
27-Sep-2010 13:31:26
Gregg, the non-object behavior is used for maps and blocks that are either being used later as object specs, or blocks and parens that will be executed later. The to-set-word is just for consistency in those cases; objects don't need it.

The 'set arg is slightly annoying, agreed. Keyword args are usually lit-words in the argument list so you don't have to specify them as lit-words at call time. If you like, the particular comparison I used allows the 'set arg to be any word type, so you can use /set or #set if you prefer. It would be less efficient for it to be #[none] instead, as more comparisons would be needed.

As for the intended purpose of extend, you're looking at it (minus the :val guard). Initially it was used for simple object and object-like extension, but sometimes we need to do more interesting stuff and this is a good way to do that. The primary purpose of extend is to allow non-guru programmers to do common guru programmer tricks with objects and such. It's a convenience function.

Gregg Irwin
28-Sep-2010 14:20:06
Maybe I'll like it once I use it, but I'm still not sold on the map/block behavior. The name doesn't match that behavior well IMO.

Post a Comment:

You can post a comment here. Keep it on-topic.

Name:

Blog id:

CS-0488


Comment:


 Note: HTML tags allowed for: b i u li ol ul font p br pre tt blockquote
 
 

This is a technical blog related to the above topic. We reserve the right to remove comments that are off-topic, irrelevant links, advertisements, spams, personal attacks, politics, religion, etc.

Updated 22-Oct-2017   -   Copyright Carl Sassenrath   -   WWW.REBOL.COM   -   Edit   -   Blogger Source Code