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