Hack
[1/21] from: rotenca::telvia::it at: 27-Dec-2001 1:34
I have found a not so standard method to find the funtion name and the code
inside a function. It works also if the funtion name is local:
hackme: func ["hackme" /local myself] [
error? err: disarm try [2 / 0]
myself: get in err 'where
print [
"my name is:" myself
"^/my body is:" mold second get myself
"^/my spec is:" mold third get myself
]
]
x
What do you think of this method? Can be considered valid in all the
situations?
---
Ciao
Romano
[2/21] from: sunandadh:aol at: 27-Dec-2001 20:17
Hi Romano,
> I have found a not so standard method to find the funtion name and the code
> inside a function. It works also if the funtion name is local:
<snip>
> What do you think of this method? Can be considered valid in all the
> situations?
I think it's a very clever hack!
Unfortunately it doesn't work for a function in ab object:
HackO: make Object! [
hackF: func ["hackF" /local myself] [
error? err: disarm try [2 / 0]
myself: get in err 'where
print [
"my name is:" myself
"^/my body is:" mold second get myself
"^/my spec is:" mold third get myself
]
]
]
HackO/HackF
I'm disappointed with Rebol that something as simple as "What's the name of
the current function?" isn't easily available, something like:
print System/Current/Function-name
A few weeks back I spent a while looking through the System object for a call
stack. It must exist, and does exist for "Do Script.r" but I couldn't find it
normal functions
. They must be hiding it from us!
If RT would put the stack in the system object along with everything else,
it'd make formatting useful debugging reports a lot more useful.
Sunanda.
[3/21] from: joel:neely:fedex at: 28-Dec-2001 6:22
Hi, Sunanda,
[SunandaDH--aol--com] wrote:
> I'm disappointed with Rebol that something as simple as
> "What's the name of the current function?" isn't easily
> available, something like:
>
> print System/Current/Function-name
>
Unfortunately, I'm too dense to parse the phrase "name of
the current function", at least WRT REBOL.
Consider the following:
>> f: reduce [
[ func [n] [n + 1]
[ func [n] [n * n - n + 1]
[ func [n] [n * n * n + 3]
[ ]
== [func [n][n + 1] func [n][n * n - n + 1]
func [n][n * n * n + 3]]
>> foreach ff f [print ff 3]
4
7
30
>>
None of those functions even *have* a name, or rather they
all have the same name, or ... hmmmm ;-)
The real issue is that functions are first-class values in
REBOL (i.e., just as with strings or numbers, they can be
created at will, passed as arguments, returned as the result
of an evaluation, etc...) As a consequence of this fact,
it is as meaningful/less to talk about the "name of the
current function" as it is to talk about the "name of the
current 2", IMHO.
--
; sub REBOL {}; sub head ($) {@_[0]}
REBOL []
# despam: func [e] [replace replace/all e ":" "." "#" "@"]
; sub despam {my ($e) = @_; $e =~ tr/:#/.@/; return "\n$e"}
print head reverse despam "moc:xedef#yleen:leoj" ;
[4/21] from: ammonjohnson:ya:hoo at: 28-Dec-2001 11:26
LOL you slay me!!
Ammon
[5/21] from: greggirwin:mindspring at: 28-Dec-2001 12:24
Hi Joel, et al
<< Unfortunately, I'm too dense to parse the phrase "name of
the current function", at least WRT REBOL. >>
How about "the identifier referencing the current context" then? :)
While it's true that functions can be anonymous, and can also be treated as
first class values, there will likely be a large body of code out there that
doesn't make use of those alternatives don't you think?
I haven't played with Romano's idea yet but I'm grateful for anything that
can help provide some kind of context information when an error occurs, even
if it won't work 100% of the time. Something is better than nothing IMO.
--Gregg
[6/21] from: sunandadh:aol at: 28-Dec-2001 14:45
Hi Joel,
> The real issue is that functions are first-class values in
> REBOL (i.e., just as with strings or numbers, they can be
<<quoted lines omitted: 3>>
> current function" as it is to talk about the "name of the
> current 2", IMHO.
That's clever stuff, but I think it misses the point I'd like to make. Take
these three functions:
FuncA: func [] [2 / 2 FuncB]
FuncB: func [] [2 / 2 FuncC]
FuncC: func [] [2 / 2]
Change any of those 2 / 2 to 2 / 0, invoke FuncA and you'll get a message
like:
** Math Error: Attempt to divide by zero
** Where: FuncC
** Near: 2 / 0
With the 'Where giving me the name of the function. The only way we know to
get that name is (see Romano's original post) to force an error.
That is not a first-class API in my opinion <g>. And all the more so as the
'Where field is described in the Core User Guide (2.3 Sept 2000) as
reserved
so we can't rely on that method long term.
I agree that if a function isn't named then Rebol can't show a name....But it
doesn't follow that therefore it can't give me any names at all, ever.
The Rebol way? IOTYWYWTKIYBSF
I'll only tell you what you want to know if you break something first
Sunanda.
[7/21] from: rotenca:telvia:it at: 28-Dec-2001 21:16
Hi Gregg and all,
I forgot the un-named func, the new version of hackme follow this message.
In the example below one can see that the first time the function is called,
the name x is not assigned. I thinked that in a statement like:
do x: does [print a]
the sequence would be:
1) create func
2) assign it to x
3) do it
But the 'where field of error! seems to think different.
(I have not seen the message of Sunanda on this thread, it seems to me that
the mailing list works bad - i receive only 2/3 message/day and often with
great delay from post also 4-5 days).
--------
do x: func ["hackme" /local myself] [
error? err: disarm try [2 / 0]
either word? myself: get in err 'where [
print ["My name is:" :myself]
myself: get myself
] [
print "Not called using a word"
]
print [
"My body is:" mold second :myself
"^/My spec is:" mold third :myself
]
]
x
---
Ciao
Romano
[8/21] from: rotenca:telvia:it at: 28-Dec-2001 21:31
Hi Sunanda and all,
I've read the Sunanda msg on escribe: you are right, hackf doesn't work in
your example.
But the reason is not that it is defined in a object, but that it is called
with a path. Seems that where uses exactly the word of the path refinement
which is normally a global word.
Look at these examples (which use the old version of hack):
Hacko: make Object! [
hackF: func ["hackF" /local myself] [
error? probe err: disarm try [2 / 0]
myself: get in err 'where
print [
"my name is:" myself
"^/my body is:" mold second get myself
"^/my spec is:" mold third get myself
]
]
hackf
]
do in hacko 'hackf
hacko/hackf
do bind [hacko/hackf] in hacko 'self
Only the third time the hack fails.
---
Ciao
Romano
[9/21] from: rotenca:telvia:it at: 28-Dec-2001 22:22
HI Joel,
> The real issue is that functions are first-class values in
> REBOL (i.e., just as with strings or numbers, they can be
<<quoted lines omitted: 3>>
> current function" as it is to talk about the "name of the
> current 2", IMHO.
name-of-current-two: 2
;-)
---
Ciao
Romano
[10/21] from: sunandadh:aol at: 28-Dec-2001 17:11
Hi Romano,
> I've read the Sunanda msg on escribe: you are right, hackf doesn't work in
> your example.
<<quoted lines omitted: 7>>
> do bind [hacko/hackf] in hacko 'self
> Only the third time the hack fails.
I think you mean the 2nd fails, the first and last work.
Unfortunately the 2nd is (in my work at least) just about the only way I ever
write function calls to functions in objects.
---
On a related subject, I'd like a way to get the name of an object. Example:
MyObject: make object! [a: 0 b: 0 c: 0]
MyFunc MyObject
...
MyFunc: func [obj [Object!]][
if error? [obj/a] [
Print [?? "is missing its 'a' variable"]
] ; if
] ;func
where we replace the "??" with the magic hack to dereference obj into
MyObject
.
Now I know I could sort of do it like this:
MyObject: make object! [a: 0 b: 0 c: 0]
MyFunc "MyObject"
...
MyFunc: func [obj-String [String!] /local obj][
obj: get to-word obj-string
if error? try [obj/a] [
Print [Obj-string "is missing its 'a' variable"]
] ; if
] ;func
But that feels like a workaround to me, and for precise functional
equivalence, I need to add in the check that Obj is an object.
Any ideas?
Sunanda.
[11/21] from: joel:neely:fedex at: 28-Dec-2001 17:37
Hi, Gregg,
Please see my reply to similar comments from Sunanda. I'm really
asking whether there's something else we might get that would be
at least as useful (if not more so) than the somewhat arbitrary
idea of "name".
Gregg Irwin wrote:
> How about "the identifier referencing the current context" then?
> :)
<<quoted lines omitted: 6>>
> when an error occurs, even if it won't work 100% of the time.
> Something is better than nothing IMO.
I tend to side with Will Rogers. "It ain't what I don't know that
gets me in trouble; it's what I know that ain't so!"
If the something is based on a potentially misleading model of
what the language is doing, then I'd rather spend my time coming
up with a better something...
-jn-
[12/21] from: joel:neely:fedex at: 28-Dec-2001 17:50
Hi, again, Sunanda,
[SunandaDH--aol--com] wrote:
> On a related subject, I'd like a way to get the name of an object.
> Example:
<<quoted lines omitted: 8>>
> where we replace the "??" with the magic hack to dereference obj
> into "MyObject".
If you really want your objects to have human-readable identities,
you can add that easily:
MyObject: make object! [ID: "George" a: 0 b: 0 c: 0]
MyFunc: func [obj [object!]]
[ if error? [obj/a]
[ print [obj/ID "is missing its 'a' member"]]
]
which also works in the more general, anonymous cases:
MyObjects: copy []
MyProto: make object! [ID: "none" a: 0 b: 0 c: 0]
repeat i 20
[ append MyObjects make MyProto [ID: join "obj-" i]]
foreach ob MyObjects [MyFunc ob]
for i 1 20 1 [MyFunc MyObjects/:i]
> ... for precise functional
> equivalence, I need to add in the check that Obj is an object.
>
Which demonstrates that:
1) REBOL really doesn't support the conventional view of objects
very well, since I don't have any way to use the function
argument spec to state that I only what a particular *kind*
of object...
2) Perhaps the problem could be solved by making sure that MYFUNC
never gets called on anything that wasn't created from an
appropriate prototype -- but that's a code design issue and
(see the previous point) not something that REBOL explicitly
supports with language mechanisms.
-jn-
[13/21] from: joel:neely:fedex at: 28-Dec-2001 18:00
[
This is a repost, as the first attempt seems to have disappeared.
If you get duplicates, please accept my apologies.
]
Hi, Sunanda,
[SunandaDH--aol--com] wrote:
> That's clever stuff...
>
I'm not trying to be "clever" to be difficult! ;-)
My concern, which I probably didn't state well, is that I'd rather
see a mechanism that works for the general case of anonymous
functions (even if -- and I agree with you -- the majority of
code uses defined-and-named-once functions) rather than have a
tool that only works in the simple cases.
> ...but I think it misses the point I'd like
> to make. Take these three functions:
<<quoted lines omitted: 9>>
> we know to get that name is (see Romano's original post) to force
> an error.
But, see below...
> That is not a first-class API in my opinion <g>...
>
Agreed. Absolutely.
> I agree that if a function isn't named then Rebol can't show a
> name....But it doesn't follow that therefore it can't give me
> any names at all, ever.
>
But it does raise in my mind the question of whether the name is
as meaningful as some other possible information we might get.
Consider the following...
>> f: reduce [
[ func [x] [x / divisor + 1]
[ func [x] [x / divisor + 2]
[ func [x] [x / divisor + 3]
[ ]
== [func [x][x / divisor + 1] func [x][x / divisor + 2] ...
>> foreach ff f [print ff 4]
3
4
5
>> divisor: 0
== 0
>> foreach ff f [print ff 4]
** Math Error: Attempt to divide by zero
** Where: ff
** Near: x / divisor + 1
Knowing that I happened to have accessed the function via the
(temporary!) reference in a word named FF is of little use, if what
I am really trying to do is find (and, presumably, fix) the defective
code.
Also, consider the following...
>> do func [x][x / divisor + 4] 5
** Math Error: Attempt to divide by zero
** Where: func [x][x / divisor + 4]
** Near: x / divisor + 4
>> g: func [n [number!]] [
[ if 0 < do func [x][x / divisor + 4] n [
[ print "positive"
[ ][
[ print "non-positive"
[ ]
[ ]
>> g 5
** Math Error: Attempt to divide by zero
** Where: func [x][x / divisor + 4]
** Near: x / divisor + 4
in which "Where:" doesn't even have a name, because there isn't one
at all.
By way of contrast, let me mention the behavior of the CALLER
function in Perl. It accesses information in the "stack of current
subroutine calls", which includes the source file name and line
number of each pending evaluation. Knowing where a function is
defined seems to me to be at least as useful as what name the caller
may have used to get to it...
Just my $0.02...
-jn-
[14/21] from: al:bri:xtra at: 29-Dec-2001 11:55
> How about "the identifier referencing the current context" then?
'self is pretty good.
>> o: make object! [m: 123]
>> probe o/self
make object! [
m: 123
]
>>
Andrew Martin
ICQ: 26227169 http://valley.150m.com/
[15/21] from: joel:neely:fedex at: 28-Dec-2001 17:34
Hi, Sunanda,
[SunandaDH--aol--com] wrote:
> That's clever stuff...
>
I'm not trying to be "clever" to be difficult! ;-)
My concern, which I probably didn't state well, is that I'd rather
see a mechanism that works for the general case of anonymous
functions (even if -- and I agree with you -- the majority of
code uses defined-and-named-once functions) rather than have a
tool that only works in the simple cases.
> ...but I think it misses the point I'd like
> to make. Take these three functions:
<<quoted lines omitted: 9>>
> we know to get that name is (see Romano's original post) to force
> an error.
But, see below...
> That is not a first-class API in my opinion <g>...
>
Agreed. Absolutely.
> I agree that if a function isn't named then Rebol can't show a
> name....But it doesn't follow that therefore it can't give me
> any names at all, ever.
>
But it does raise in my mind the question of whether the name is
as meaningful as some other possible information we might get.
Consider the following...
>> f: reduce [
[ func [x] [x / divisor + 1]
[ func [x] [x / divisor + 2]
[ func [x] [x / divisor + 3]
[ ]
== [func [x][x / divisor + 1] func [x][x / divisor + 2] ...
>> foreach ff f [print ff 4]
3
4
5
>> divisor: 0
== 0
>> foreach ff f [print ff 4]
** Math Error: Attempt to divide by zero
** Where: ff
** Near: x / divisor + 1
Knowing that I happened to have accessed the function via the
(temporary!) reference in a word named FF is of little use, if what
I am really trying to do is find (and, presumably, fix) the defective
code.
Also, consider the following...
>> do func [x][x / divisor + 4] 5
** Math Error: Attempt to divide by zero
** Where: func [x][x / divisor + 4]
** Near: x / divisor + 4
>> g: func [n [number!]] [
[ if 0 < do func [x][x / divisor + 4] n [
[ print "positive"
[ ][
[ print "non-positive"
[ ]
[ ]
>> g 5
** Math Error: Attempt to divide by zero
** Where: func [x][x / divisor + 4]
** Near: x / divisor + 4
in which "Where:" doesn't even have a name, because there isn't one
at all.
By way of contrast, let me mention the behavior of the CALLER
function in Perl. It accesses information in the "stack of current
subroutine calls", which includes the source file name and line
number of each pending evaluation. Knowing where a function is
defined seems to me to be at least as useful as what name the caller
may have used to get to it...
Just my $0.02...
-jn-
[16/21] from: sunandadh:aol at: 28-Dec-2001 19:35
Hi Joel,
> Knowing that I happened to have accessed the function via the
> (temporary!) reference in a word named FF is of little use, if what
> I am really trying to do is find (and, presumably, fix) the defective
> code.
(Replying to several emails in one go, and may losing part of the thread in
the process).
Absolutely, I agree. I want first-class diagnostic information, and some of
my questions over the past couple of weeks have been about this (e.g. the
thread on "Better Error Messages?"
We probably have different sets of priorities. I'll tell you mine, so you can
see where I'm coming from (and then you can tell me where to go!). I'm still
trying out Rebol, wanting to see if it is possible to build largish
applications with this basic structure:
Rebol []
Do Preparation
ShutDown: false
While [not ShutDown]
[
if Error? Try [Do RunApplication]
[If Error? Try [Do ReportIncident
Do AttemptRecovery
] ;try
[ShutDown: True]
] ;if
] ;while
Where:
--Preparation does what ever is necessary in advance for error recovery and
quality control (For example, I've built one pilot application which
checksums the *.r sources to ensure the distribution set is consistent and
untampered with).
RunApplication is the payload (the pilot is about 6,000 lines of Rebol as
measured out of Mold).
ReportIncident should dump (or email or whatever) scads of useful stuff about
where and why we've failed: failing function name, complete call stack,
status of ports, maybe molds of crucial data structures). This is the weakest
part of the pilot. it doesn't do much more than print the error object. And
that gives barely a hint of where in the 6000 lines we're a-pumping mud.
AttemptRecovery is application-specific code to repair (or direct the user to
repair) the damage.
I've been conscientiously putting bugs into code for over 30 years now, and
even back then the first thing a diagnostic tool would tell me is "last known
function call was X. Call stack is ...". Maybe I'm hopelessly old-fashioned
butI can't believe the programming world has made so much progress that these
things are now impossible <wry g>.
I've toyed with ideas like object identifiers (as suggested in another of you
emails), but that only works for my objects, not for any third-party ones I
might be using (unless it becomes a standard method). And I've experimented
with hand-coding a call stack:
Anyfunc: [...] [
Stack "AnyFunc"
....
Unstack
Return true
]
Where Stack and Unstack simple append/remove the string in a global block.
But it's a real hassle to remember to do it for every return statement.
I think maybe the real problem (from my perspective) is that RT haven't
delivered what is needed in the field as opposed to the lab. But they are not
far off: if they action a couple of feedback requests, I'll be very happy.
Sunanda.
[17/21] from: rotenca:telvia:it at: 29-Dec-2001 2:03
Hi Sunanda,
> > Only the third time the hack fails.
>
> I think you mean the 2nd fails, the first and last work.
No, the third:
Hacko: make Object! [
hackF: func ["hackF" /local myself] [
error? probe err: disarm try [2 / 0]
myself: get in err 'where
print [
"my name is:" myself
"^/my body is:" mold second get myself
"^/my spec is:" mold third get myself
]
]
hackf ; <- 1 from inside the object : works
]
do in hacko 'hackf ; <- 2 : works
hacko/hackf ; <- 3 : fails
do bind [hacko/hackf] in hacko 'self ;<- 4 : works (if the program is not
stopped by previous error)
And works also if the function is called with a word local to a function or to
a Use block.
The only failure case is the path, but if one does not know how the function
is called, he can't
be sure about what the word points to.
If we know that the function is defined in an object we could try to bind the
word to the object context, but we must know at least a word of the context or
be sure that 'self has not been redefined.
> On a related subject, I'd like a way to get the name of an object. Example:
> MyObject: make object! [a: 0 b: 0 c: 0]
<<quoted lines omitted: 7>>
> where we replace the "??" with the magic hack to dereference obj into
> "MyObject".
If you write the objects, you could do something like:
make-ob-name: func [word [word!] spec [block!]][
set word make object! compose [(spec) self-name: (bind reduce [to-lit-word
word] word)]
]
make-ob-name 'foo [a: 2]
probe foo
probe foo/self-name
probe get foo/self-name
where self-name contains a word (eventually local) which is a pointer to the
object.
It what do RT with some Vid faces.
> Now I know I could sort of do it like this:
> MyObject: make object! [a: 0 b: 0 c: 0]
<<quoted lines omitted: 8>>
> But that feels like a workaround to me, and for precise functional
> equivalence, I need to add in the check that Obj is an object.
I'd not use string, because they works only with object defined in the global
context (to-word creates always a global word). My try:
MyFunc: func [word [word!] /local obj][
obj: get word
if all [object? obj error? try [obj/a]] [
Print [mold word "is missing its 'a variable"]
] ; if
] ;func
But you should check the error type to be sure.
However to know if a word is defined in an object you can do a more simple:
if none? in obj 'a [print ["'a is not defined in" mold 'obj]]
Ciao
Romano
[18/21] from: sunandadh:aol at: 29-Dec-2001 5:06
Hi Romano,
> > > Only the third time the hack fails.
> >
> > I think you mean the 2nd fails, the first and last work.
>
> No, the third:
Thanks for the correction.
In case you didn't pick it up in my earlier email, the Sept 2000 (the latest,
I think) Rebol/Core User Guide says of the Error/Where field
"The where field is reserved"
This is the only place I can find in that manual where something is
specifically designated reserved.
That means your hack may not work in future versions. But I hope also it
means that RT are planning to turn 'Where into a richly meaningful object of
error-context information.
<snip>
> If you write the objects, you could do something like:
<snip>
Another nice Rebol hack!
It's not immediately useful to me as I tend to have objects "two deep". I
could use it at the first level:
make-ob-name 'f [a: 1 b: 2]
but to base an object on that, I still need to write
g: make f [c: 3] g/self-name: 'g
So it's as easy to follow Joel's suggestion of coding the identifier in the
object.
Sunanda.
[19/21] from: rotenca:telvia:it at: 30-Dec-2001 14:38
Hi, Sunanda
> In case you didn't pick it up in my earlier email, the Sept 2000 (the
latest,
At the end the mail arrived to me, with a great delay.
> I think) Rebol/Core User Guide says of the Error/Where field
>
> "The where field is reserved"
>
> This is the only place I can find in that manual where something is
> specifically designated reserved.
This leaves us only with a 5 words copy of source in Near.
> It's not immediately useful to me as I tend to have objects "two deep". I
> could use it at the first level:
>
> make-ob-name 'f [a: 1 b: 2]
>
> but to base an object on that, I still need to write
>
> g: make f [c: 3] g/self-name: 'g
make-ob-name: func [word [word!] type [object! datatype!] spec [block!]][
set word make type compose [self-name: (bind reduce [to-lit-word word]
word) (spec)]
]
But this is only an example. There are many methods to works with these kind
of things.
You could keep also an history of names of derived object in self-name, using
a block instead of a word.
---
Ciao
Romano
[20/21] from: sunandadh:aol at: 30-Dec-2001 15:52
Hi Romano,
> I forgot the un-named func, the new version of hackme follow this message.
<snip>
Another case of unnamed functions is VID action facets.
Here I've adapted you code to work in that case, I don't know if there is
another way to specify a /local in a Vid action facet, but I've used 'Use to
get the same effect. otherwise, it's all your code.
unview/all
view layout [
button "Print me" [ use [err myself] [
error? err: disarm try [2 / 0]
either word? myself: get in err 'where [
print ["My name is:" :myself]
myself: get myself
] [
print "Not called using a word"
]
print [
"My body is:" mold second :myself
"^/My spec is:" mold third :myself
]
] ;use
] ; action
] ;layout
Though it would be kinda neat if the Face was assigned to a variable and the
code could print that variable name!
Print-Button: Button "Print me" [...?code?...]
Prints "My name is Print-Button/Action"
But that's beyond my skills
Sunanda.
[21/21] from: rotenca:telvia:it at: 31-Dec-2001 2:13
Hi Sunanda,
> Here I've adapted you code to work in that case, I don't know if there is
> another way to specify a /local in a Vid action facet, but I've used 'Use to
Good choice, i think.
BTW, to change the spec of all action func, you could change the function:
face/multi/block:
func[face blk][
if pick blk 1 [
face/action: func [face value] pick blk 1
if pick blk 2 [face/alt-action: func [face value] pick blk 2]
]
]
or change directly the face/action func after creation.
---
Ciao
Romano
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted