oops.. just one little detail... 'SAME? isn't a mezz.. its a native... -MAx
On Mon, Sep 14, 2009 at 4:56 PM, Maxim Olivier-Adlhoch <[email protected]> w= rote: > Hi Giuseppe, > > Sorry I didn't check the ML for a few days and yours slipped in the crack= s! > > Here is a long and verbose explanation of most of what happens. =A0If > 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. =A0;-) > > I'll start by reposting the original code snippet as a reference: > > person: context [ > =A0 uid: func [/set value][data: [#[none]] =A0either set [data/1: value][= data/1]] > =A0 name: "me" > ] > .... >>> =A0person/uid/set 222 > =3D=3D 222 >>> managers/ceo > =3D=3D 222 > > > about functions as values > ------------------------------------ > Basically, all rebol datatypes are evaluated when they are encountered > by the interpreter. =A0because 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. =A0This 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). =A0This block is actually part of the 'UID function's > context when the function is created. =A0So 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 =A0(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. =A0Its a bit like saying get the first > value pointed to by the block pointed to by 'DATA. =A0(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. =A0This 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. =A0For example, if I had done this: > > employee: make person [ > =A0 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: =A0make object!) > > employee: context [ > =A0 uid: get in person 'uid > =A0 title: "Workaholic" > ] > > Here you see that we tell 'EMPLOYEE to use the exact same function as > the one in person. =A0We '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 =A0**another** object using 'SELF... =A0:-) > > ;------------------------ the code ------------------------- > > person: context [ > =A0 =A0full-name: ["luke" "skywalker"] > =A0 =A0first-name: func [/set val][either set [self/full-name/1: > val][self/full-name/1]] > =A0 =A0last-name: func [/set val][either set [self/full-name/2: > val][self/full-name/2]] > ] > > employee: context [ > =A0 =A0first-name: get in person 'first-name > =A0 =A0last-name: get in person 'last-name > =A0 =A0full-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. =A0 '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 =A0make. =A0Meaning, it will evaluate whatever value is > referred to by the words 'FIRST-NAME and 'LAST-NAME within 'EMPLOYEE. > > BUT!!! =A0These, as we just explained, are values taken from the other > object, namely 'PERSON. =A0So basically, you have an object calling > methods from another object, directly. =A0This 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 > =3D=3D ["luke" "skywalker"] >>> employee/first-name/set "bill" > =3D=3D "bill" >>> employee/full-name > =3D=3D ["bill" "skywalker"] >>> person/full-name > =3D=3D ["bill" "skywalker"] >>> same? person/full-name/1 employee/first-name > =3D=3D true >>> same? person/first-name employee/first-name > =3D=3D 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 ? =A0:-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 [ > =A0 =A0button with [ > =A0 =A0 =A0 =A0texts: =A0reduce [ does [to-string counter] does [random "= ABCDE"]] > =A0 =A0 =A0 =A0colors: reduce [ does [random white] does [random white]] > =A0 =A0 =A0 =A0effects: reduce [ > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0does [reduce ['gradient 0x1 color color - = 75.75.75]] > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0does [reduce ['gradient 0x-1 color color -= 75.75.75]] > =A0 =A0 =A0 =A0] > =A0 =A0] > =A0 =A0feel [ > =A0 =A0 =A0 =A0 =A0 =A0redraw: func [face act pos /local state][ > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0counter: counter + 1 > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if all [face/texts face/texts/2] [ > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0face/text: either face/state [face= /texts/2] [face/texts/1] > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0] > =A0 =A0 =A0 =A0 =A0 =A0if face/edge [face/edge/effect: pick [ibevel bevel= ] face/state] > =A0 =A0 =A0 =A0 =A0 =A0state: either not face/state [face/blinker] [true] > =A0 =A0 =A0 =A0 =A0 =A0if face/colors [face/color: either face/state > [face/colors/2] [face/colors/1]] > =A0 =A0 =A0 =A0 =A0 =A0if face/effects [face/effect: either face/state > [face/effects/2] [face/effects/1]] > =A0 =A0 =A0 =A0 =A0 =A0] > =A0 =A0] > ] > > 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. =A0whenever 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 > <[email protected]> wrote: >> >>> person: context [ >>> =A0 =A0uid: func [/set value][data: [#[none]] =A0either set >>> [data/1: value][data/1]] >>> =A0 =A0name: "me" >>> ] >> ..... >>> >> =A0person/uid/set 222 >>> =3D=3D 222 >>> >> managers/ceo >>> =3D=3D 222 >>> >> >>> >>> this is one way to use the function hack, but there are many >>> others, and they depend on the application itself. >>> >>> there are a few little details when using the hack, but >>> usually, its pretty invisible. >> >> Hello Maxim, I have read your interesting post but I want the details. H= ow >> it works ? What happens underneat (at interpeter and structure details) = when >> you read the value of UID using another object like "managers/ceo" =A0? >> Also, I whis to know the notes you have omitted about this approach and >> eventually which other approach exists... >> There are a lot of things I still have to know about objects. >> >> Giuseppe Chillemi >> >> -- >> To unsubscribe from the list, just send an email to >> lists at rebol.com with unsubscribe as the subject. >> >> > -- To unsubscribe from the list, just send an email to lists at rebol.com with unsubscribe as the subject.
