Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search

[REBOL] Re: R: Re: R: Re: Links ? Pointers ?

From: moliad:gm:ail at: 14-Sep-2009 16:56

Hi Giuseppe, Sorry I didn't check the ML for a few days and yours slipped in the cracks! Here is a long and verbose explanation of most of what happens. If something isn't clear, ask a precise question and I'll do my best to explain it further. so here we go... saddle-in, this is going to be a big ride into the depths of some REBOL Guru level secrets. ;-) I'll start by reposting the original code snippet as a reference: person: context [ uid: func [/set value][data: [#[none]] either set [data/1: value][data/1]] name: "me" ] ....
>> person/uid/set 222
== 222
>> managers/ceo
== 222 about functions as values ------------------------------------ Basically, all rebol datatypes are evaluated when they are encountered by the interpreter. because functions require no more syntax than a normal variable, they can be silently "slipped-in" as if they WHERE a simple variable (cause in fact they are! :-). The 'UID function, when created by the 'FUNC function creates a context for itself. This context persists for as long as the function exists. If you notice the following: data: [#[none]] I assign a block! to 'DATA (I **DON'T** copy it at each function evaluation). This block is actually part of the 'UID function's context when the function is created. So it persists throughout that function's existence... This means every time 'UID is evaluated, 'DATA really is the same block, every time. When I simply ask for the value of 'UID (person/uid), the function is evaluated as normal, and it returns the first data of the persistent block. When I use the function's /SET refinement, I simply stores a new value into this persistent block: data/1: value As a side note, remember that you can use blocks as indirections, a bit like double referencing in C. Its a bit like saying get the first value pointed to by the block pointed to by 'DATA. (I prefer to use the term "refers" and "contains" respectively though). About sharing functions across objects: -------------------------------------------------------- Normally, when you make objects and use another object as the basis, all of the functions are copied to the new object bound and bound to it. This means that any words which are present in the body of the function will now refer to those found in that object, or to those in the arguments of the function. For example, if I had done this: employee: make person [ title: "Workaholic" ] And tried the function trick, the 'UID function inherited from 'PERSON would not be shared because 'EMPLOYEE would contain a New & independent copy of the 'UID function with its own persistent function context. The trick is to tell 'MAKE, what we we explicitly want the 'UID to be: ('CONTEXT is a mezzanine function which simply does: make object!) employee: context [ uid: get in person 'uid title: "Workaholic" ] Here you see that we tell 'EMPLOYEE to use the exact same function as the one in person. We 'GET it from the other object, and 'MAKE will assign it to the new object, at the location referred to by word 'UID in this new object. It won't be bound to the new 'EMPLOYEE object, and 'UID will actually be the same function as person/uid, NOT a copy of it. This is a VERY powerful feature of REBOL. From this point on, employee/uid and person/uid, being the same function, really share the same function context, so will set and get the same value. Function hack level 2 ----------------------------- You said you wanted other examples of the function trick... Here we'll go Fubar weird!!! Lets refer to values of **another** object using 'SELF... :-) ;------------------------ the code ------------------------- person: context [ full-name: ["luke" "skywalker"] first-name: func [/set val][either set [self/full-name/1: val][self/full-name/1]] last-name: func [/set val][either set [self/full-name/2: val][self/full-name/2]] ] employee: context [ first-name: get in person 'first-name last-name: get in person 'last-name full-name: func [][reduce [first-name last-name]] ] ;-------------------------------------------------------------- Within 'PERSON, we create a similar function-as-value trick, but this time, we use 'SELF to refer to the context (object!) the function is bound to, and then interact with another member of that object using path notation. 'EMPLOYEE, if you recognize the pattern explained earlier, simply borrows ('GETs) two of those functions-as-values and stores them in its own context... but notice a HUGE detail. 'SELF in these functions, still refers to the 'PERSON object, because the functions are still bound to 'PERSON !!! 'FULL-NAME within 'EMPLOYEE, is bound normally since it is created by 'EMPLOYEE 's make. Meaning, it will evaluate whatever value is referred to by the words 'FIRST-NAME and 'LAST-NAME within 'EMPLOYEE. BUT!!! These, as we just explained, are values taken from the other object, namely 'PERSON. So basically, you have an object calling methods from another object, directly. This is far beyond the level of control you can achieve with traditional inheritance and polymorphism, IMHO. You could go a step further and if you really wanted to go nuts with this, you could change the person which is assigned to the function-as-value referred by 'EMPLOYEE on the fly (but I won't go there for risk of loosing you for now ;-) All of this is AFAIK, a feature very few, if any, other languages allow you to do so "legally", safely and with so much detail and dynamic control. here are a few lines of console code which play around with the above level 2 function hack:
>> employee/full-name
== ["luke" "skywalker"]
>> employee/first-name/set "bill"
== "bill"
>> employee/full-name
== ["bill" "skywalker"]
>> person/full-name
== ["bill" "skywalker"]
>> same? person/full-name/1 employee/first-name
== true
>> same? person/first-name employee/first-name
== true 'SAME is a mezzanine function that only returns True if its the same string, not just by its text content like equal?... it really is the same string "pointer" in memory. sweet isn't it ? :-D about other uses of the function hack ----------------------------------------------------- you might want to replace a value by an SQL statement function-as-a-value which is executed every time a value is needed... and if you set the value, it is inserted back directly... this way, you are sure that your data is coherent. In VID you can put functions instead of values for some facets... example are colors, texts & effects which change every time the face is shown... with a little tweak in the redraw : example: view layout [ button with [ texts: reduce [ does [to-string counter] does [random "ABCDE"]] colors: reduce [ does [random white] does [random white]] effects: reduce [ does [reduce ['gradient 0x1 color color - 75.75.75]] does [reduce ['gradient 0x-1 color color - 75.75.75]] ] ] feel [ redraw: func [face act pos /local state][ counter: counter + 1 if all [face/texts face/texts/2] [ face/text: either face/state [face/texts/2] [face/texts/1] ] if face/edge [face/edge/effect: pick [ibevel bevel] face/state] state: either not face/state [face/blinker] [true] if face/colors [face/color: either face/state [face/colors/2] [face/colors/1]] if face/effects [face/effect: either face/state [face/effects/2] [face/effects/1]] ] ] ] here face/colors/2 is evaluated each time the face is drawn... so you might want to put an sql statement which checks if an error occurred on the db for example. whenever the face is refreshed it will be up to date... which is pretty useful. the view face is not a normal object so most of the primitive facets (like color) will not respond if they are set to functions because the view engine actually uses the values directly. as an example if you put a function in face/text it will actually display ?function? as the text... but it should give you ideas on other uses... just the same. -MAx On Thu, Sep 3, 2009 at 5:38 AM, Giuseppe Chillemi <> wrote: