Thanks for another remarkable post about Rebol context.
I think I actually understood parts of it :-)
I hope that in 
                 >>do x
                 happy new year!
you meant to say "do y".

Vic Hirsch



>>> <[EMAIL PROTECTED]> 08/10/00 09:57PM >>>
Hi, today I learned a little about rebol internals.
I had an object that accumulated a lot of data from 
a run, and when I ran obj/Report which was supposed 
to give me my valuable results, I discovered that 
due to a minor bug it wouldn't complete.

The object in question was too complex to just manually 
check a few fields.

I tried re-loading the script after correcting it,
but then I had a corrected prototype myobj!
and my one real instance myboj which was 
originally made with myobj:  make myobj! []

Now, I tried to repair myobj

set in myobj 'Report get in myobj! 'Report

Well, that almost worked. Now the code was 
correct as far as not having a bug, but unfortunately,
the context of the /Report function words 
and so forth were all bound to the context of myobj!
instead of myobj, and myobj! just was an empty prototype.

My initial hack would have been to try to bind 
the words in /Report to the right context, but 
I couldn't seem to make it work, and while 
I was fooling around, I accidentally killed 
'first as follows:
first: get in myobj 'Report

or something like that.
Rebol really gives you the power to blast your 
foot off, and I say, why not, it's a free 
country, ain't it?!

Well, I then found all sorts of functions 
no longer worked with first dead, and I couldn't figure out 
how to set first back to whatever native #41
it had originally been assigned to.
Maybe that's not something we're supposed to
be able to do...

Anyway, I ended up figuring out that I could 
save %myobj.txt myobj

and then I loaded up a new instance of 
Rebol.exe and did a myobj: do load %myobj.txt
And with a little more fooling around, 
I had my object back and working,
and I was eventually able to do /Report and 
get the data out that I needed.
Apparently myobj was quite happy 
to work with its words bound to the 
global context that load gives it,
but more about that later.
I also had to walk thru myobj, changing
any reducing any block values in it.
E.g. I had a block called VU: copy []
declared in myobj and after it ran 
it was full of cool stuff.

Unfortunately, "do load" ain't enough to 
get it back in shape.  You need this 
little feller, self-redux, and handy loop.

self-redux: func [
 blk [block!]
 /local newblk
][
 newblk: reduce blk
 clear blk
 insert blk newblk
]

notice that I preserve the original block,
just in case there were other references to 
that block they would still be preserved.
Had I just set blk: somenewblock then
it would have changed blk within self-redux,
but the original value passed in would still 
be bound the the words of myobj.

So, you need this to fix up loaded myboj:

self-redux myobj/VU

and if you have other objects in vu, say history: []
they will need help, too:

repeat v length? myobj/VU [
 self-redux myobj/VU/:v/history
]

and so on.
I wish there were something better than save and load.
And also, I should warn you that if you save 
an object which has a word that has been unset,
and my favorite example is the
 inBuffer: unset
which you get inside a saved closed port object.
You can cheat that too by doing a search and 
replace on the original %myobj.txt
replacing all occurences of
 inBuffer: unset
with
 inBuffer: none    unset 'inBuffer

Bizarre as it is, it works.
Of course other objects may have other special
requirements to get save and load to cooperate.
You get to feeling very religious at a time like this.
Lord, save me!

The end result of that was that I had all my data 
back and /Report gave me the goodies!


After that I played around with bind and 
tried to figure out how I would have got 
/Report re-bound.  I am still not totally 
sure how that would work, but I did 
finally dig into functions, using first,
second, and third.

It's not as evil as it looks at first,
and it is quite enlightening.

"first :somefunc" will give you the parameter list 
it is basically just a block of words including 
refinements.  You can save a reference to it in another variable
"parms: first :somefunc"
"probe parms"
"parms/3"
etc.

Note that you have to use : in front of somefunc
or else when rebol evaluates the line you 
typed in it will think somefunc needs to 
be executed instead of just grabbing the 
function value associated with the word 'somefunc


"third :somefunc" is almost the same 
thing, except that it is preserved the way you actually 
typed it and contains comments and so forth that the 
original code had when it was defined/executed, and which 
the 'help and 'source functions can take advantage of 
automatically.  It also contains the types, e.g. [block!]
if any were declared to check the param value types in
the original somefunc definition.


Obviously, the list of words in "first :somefunc" is 
used by the system when reducing and binding parameters
in evaluating expressions and can checking number
of params, etc...

But the meat of your function is in "second :somefunc"
which is basically nothing but words and literals 
and blocks, I guess.  Comments are not contained in it.
Even operators are just words?
Well you can do this:
  x: second :somefunc
  repeat i length? x [print [i type? x/:i x/:i]]

and you will get a cool list of the words,
and their types and position numbers in the block.
If you want to grab just a particular word,
you can just say x/3 to get the third item.
This is very handy.  If x/3 returns a word,
it is not just any word.  Somehow, the context 
comes along with the word, and you can insert 
that word, carrying it's context and therefore 
what happens to it when you reduce it or evaluate it,
you can insert that word into another block!!!
You can go around having a good time now!

You can also use find to find the word in the block x.
e.g. find x '+ 
would find the first occurenc of the operator +
that might occur somewhere inside that function body.

Paths are a little weird, and so on...
You can't get to the parts of a path 
as if it were a block.  All I know is 
that you might be able to get a string 
representation of the path with "s: to string! x/7"
and then s would be a string that looks like the 
path contained in the 7th position of our 
imaginary function somefunc.  You could 
then try to parse the string into parts,
rebind the right words, and then form 
a new path and then put that back into the code block.
I haven't actually done this yet, as there are 
so many other wonderful things to wonder at.

For instance,
 if you have a string like this:

z: {[
    answ: "tell me about it!"   ;;; stupid comment
    print answ
]}

>> zz: load z
== [
    answ: "tell me about it!"
    print answ
]
>>

notice that there are no comments after load is done with it.

also, while z is a string and z/1 is a single character [,
zz is a block and zz/1 is the word 'answ bound to the global context.

If you had someotherword like this
someobject:  make object! [
 answ: "pray for a miracle!"
]

someotherword: in someobject 'answ

I tried stuff like 
"bind zz/4 someotherword"
but it never worked,
even though the help on bind implies that it 
would work.

tmp: [answ]
bind tmp/1 someotherword

also doesnt work, you get no change in the word answ inside tmp.

However this does work, if I recall right:
bind tmp someotherword

for some reason, binding a block of word(s) works,
and binding a single word chokes.
But bind goes through all words in the block 
and changes their context to that of someotherword.

now we can do a little surgery:

change at zz 4 tmp/1

this should mean that 
even though zz will set
the global word 'answ to "tell me about it!",
when we execute print answ this is going  
to be the word answ in the context of 
someobject where answ equals "pray for a miracle!"

of course you could have just done this:
  change  at zz 4  someotherword
or this:
  change  at zz 4  in someobject 'answ

>>do zz
pray for a miracle!

>>answ
tell me about it!

Just as we finagled about with the block of code 
in zz,  you can fiddle around with it just as 
well if you get the third of the :somefunc,
because that is just another block.

So, now you see what load does to a string,
stripping comments, converting to words and 
blocks and other tokens, I guess.  Plus all 
of these newly created words in the load 
result have the global context bound to them.
If you just then execute, do, reduce, whatever,
the loaded block, then when each word is fetched 
and it's value is get then the value is going 
to be in the global context.  By the way, this 
kind of solves the problem of forward references,
e.g. 
Start fresh Rebol.exe,

>>value? 'y
== false

>>x: [print y ]  ;; we haven't defined y yet, oh no!

>>y: "happy new year!"

>>value? 'y
== true

>>do x
happy new year!


you will get the nice result.
How?  well, when 'y is created inside blk [print y],
it has the global context. Even though y at this 
point has not been set to anything as shown 
by the function 'value?

So, anyway, you can dick around with the block in 
the function, editing to your hearts delight,
inserting, deleting, adding words from god knows where
they been!

And you know now how to get words from other blocks 
and objects, so let the Royal Rebol Rumpus begin!

-Galt

Reply via email to