Speeding up code
[1/18] from: sunandadh::aol::com at: 13-Feb-2002 6:14
I recently tried to put some zip into some sluggish code.
I was surprised just how fast a lot of Rebol is. I instrumented several of my
suspect functions and found they were almost too fast to meter.
The slowest thing I found was (perhaps not surprisingly) Layout.
To soup up the code, I wrote an elapse timer function. It's listed below with
some samples of use. Feel free to use it.
The code is reasonably Rebollish (it uses a persistent local variable), but
I'd appreciate feedback on better ways of acheiving the same, particularly:
1. Can I avoid having to define the same refinement variable (unit-name)
twice (unit-name-start and unit-name-end)?
2. Any better suggestions for those awkward Pokes?
Thanks,
Sunanda
>>>>Sample console session:
loop 5 [
unit-timer/start "A code unit"
wait (0.001 * random 50) ;; your code here!!
unit-timer/start "Another code unit"
wait (0.02 * random 20)
print unit-timer/end "A code unit"
wait (0.1 * random 10)
print unit-timer/end "Another code unit"
print unit-timer/end "A unit with no start"
]
print mold unit-timer/report
help unit-timer
<snipped>
>>>>>The code:
rebol []
Unit-Timer: func [
{Records elapse time for named units of code
Returns:
>> False -- /End called without /Start for same unit
>> True -- /Start okay
>> decimal -- /End only: cumulative elapse time for unit
>> block -- /Report only: history block.
Known problems:
1. Doesn't check for start/end straddling midnight(s)
2. Watch out if you use this in a function -- putting
an /End call as the last thing in a function that uses an
implicit Return will send the elapse time back to your caller.
}
/Report
{Return units history block
Format is:
[string time decimal integer ...] (repeats in groups of four)
where:
>> string -- is the unit name
>> time -- is the latest start time (0 = not being timed)
>> decimal -- culmulative elapse time for unit name
>> integer -- number of times unit has been timed.
}
/Start Unit-name-start [String!]
{Start a unit.}
/End Unit-name-end [String!]
{End a unit.}
/local Units-block
Unit-data] [
Units-block: []
;; -------------------------
;; Handle /Report refinement
;; -------------------------
If Report [Return Units-block]
;; ------------------------
;; Handle /Start refinement
;; ------------------------
;;
;; For a new unit, add a history group.
;; If the unit already exists, simply
;; reset its start time. This will lose
;; some elapse time if there was an
;; outstanding /End.
if Start [
unit-data: find units-block unit-name-start
either none? unit-data [
append units-block
compose [(unit-name-start) ;; unit-name
(now/time/precise) ;; start time
0.0 ;; cumulative elapse
0 ;; invocations
]
][
poke units-block ;; (re)set start time
(1 + index? unit-data) now/time/precise
]
Return true
] ; if-start
;; ----------------------
;; Handle /End refinement
;; ----------------------
unit-data: find units-block unit-name-end
if any [none? unit-data
decimal? pick units-block (1 + index? unit-data)
] [return false] ;; End without a start
poke units-block (2 + index? unit-data)
to-decimal (unit-data/3
+ (now/time/precise - Unit-data/2))
poke units-block (3 + index? unit-data) unit-data/4 + 1
poke units-block (1 + index? unit-data) 0.0
Return unit-data/3 ;; Return cumulative elapse time for unit
] ; func
[2/18] from: joel:neely:fedex at: 13-Feb-2002 7:36
Hi, Sunanda,
[SunandaDH--aol--com] wrote:
> To soup up the code, I wrote an elapse timer function. It's
> listed below with some samples of use. Feel free to use it.
<<quoted lines omitted: 4>>
> (unit-name) twice (unit-name-start and unit-name-end)?
> 2. Any better suggestions for those awkward Pokes?
I'd refactor the code to separate the behavior of an individual
watch (as an object with its own state) from the behavior of
the total collection of watches in existence at a given time
(given a just-for-fun name ;-). Watches know how to start, stop,
and report their total times (in seconds), while the watchmaker
knows how to make a new watch, find a watch by the label placed
on it when he made it, and get reports from all of the watches.
The code below is fairly minimal, but I think it addresses most
of the capabilities you had in UNIT-TIMER (or could be extended
easily to include any I missed in a quick read-through).
It performs as follows...
The watchmaker can create new watches on demand, and a reference
to a watch *can* be kept to allow it to be used individually:
>> a: watchmaker/new "frushlinger"
>> a/start
== 7:18:24.53
>> b: watchmaker/new "schlocken"
>> b/start
== 7:18:38.37
>> c: watchmaker/new "fleegle"
>> c/start
== 7:18:58.09
The watchmaker can provide a consolidated report (each watch can
return total elapsed time whether running or not):
>> watchmaker/report-all
== ["frushlinger" 46.03 "schlocken" 32.19 "fleegle" 12.47]
>> watchmaker/report-all
== ["frushlinger" 47.95 "schlocken" 34.11 "fleegle" 14.39]
>> watchmaker/report-all
== ["frushlinger" 49.43 "schlocken" 35.59 "fleegle" 15.87]
Watches can be individually operated:
>> b/stop
== 46.25
>> watchmaker/report-all
== ["frushlinger" 61.9 "schlocken" 46.25 "fleegle" 28.34]
>> watchmaker/report-all
== ["frushlinger" 63.16 "schlocken" 46.25 "fleegle" 29.6]
The watchmaker manages the entire collection, so it's not
necessary for watch references to be kept around when not
needed -- the watchmaker can find the watch (by label) for
later operation:
>> a: b: c: none
== none
>> x: watchmaker/fetch "fleegle"
>> x/stop
== 85.96
>> watchmaker/report-all
== ["frushlinger" 125.78 "schlocken" 46.25 "fleegle" 85.96]
>> y: watchmaker/fetch "frushlinger"
>> y/stop
== 140.99
The default REPORT-ALL just returns label/time pairs in the
order the watches were created. However, the watchmaker can
provide the timings in other potentially-useful orders:
>> watchmaker/report-all
== ["frushlinger" 140.99 "schlocken" 46.25 "fleegle" 85.96]
>> watchmaker/report-all/by-label
== ["fleegle" 85.96 "frushlinger" 140.99 "schlocken" 46.25]
>> watchmaker/report-all/by-time
== ["schlocken" 46.25 "fleegle" 85.96 "frushlinger" 140.99]
Of course, the result could be processed by something like
foreach [lab tot] watchmaker/report-all [ ... ]
for whatever purpose...
After all that blabbing, here's the code:
8<------------------------------------------------------------
watchmaker: make object! [
collection: []
watch: make object! [
label: ""
total: began: ended: 0
reset: func [] [total: began: 0]
start: func [] [began: now/time/precise]
to-seconds: func [t [time!]] [
t/hour * 60 + t/minute * 60 + t/second
]
stop: func [/local ended] [
ended: now/time/precise - began
if time? began [
began: 0
total: total + to-seconds ended
]
]
report: func [] [
if time? began [stop start]
reduce [label total]
]
]
reset: func [] [collection: copy []]
new: func [lbl [string!]] [
last back insert tail collection make watch [label: lbl]
]
fetch: func [lbl [string!]] [
foreach this-watch collection [
if lbl = this-watch/label [return this-watch]
]
none
]
report-all: func [/by-label /by-time /local rpt] [
rpt: make block! length? collection
foreach this-watch collection [
insert tail rpt this-watch/report
]
if by-label [sort/skip rpt 2]
if by-time [sort/skip/compare rpt 2 2]
rpt
]
]
8<------------------------------------------------------------
HTH!
-jn-
--
; 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" ;
[3/18] from: sunandadh:aol at: 13-Feb-2002 10:23
Hi Joel,
> I'd refactor the code to separate the behavior of an individual
> watch (as an object with its own state) from the behavior of
> the total collection of watches in existence at a given time
Thanks for the complete rewrite. It's always useful to get a completely
different view on how to do something, and I hope that between us we've
produced some functionality that is of use to others.
A few thoughts about the difference between object+functions and
function+refinements, inspired by your rewrite.
Object+functions of course give a lot more room to breathe -- it is easier to
have persistent variables in them than it is with function+refinements.
>From the outside there is almost no difference between calling an
object/function and a function/refinement. If I rewrote my code to be an
object but with the same interface, you couldn't tell the difference from
seeing the invocation:
>> unit-timer/start "A code unit"
I say _almost_ no difference for two reasons:
01 -- In the original code, an invocation without a refinement executes the
code and returns False:
>> print unit-timer
== false
If it was an object, invocating the object name alone returns the object:
>> print unit-timer
?object?
There is no implicit init code for a Rebol object.
02 -- Help Unit-timer in the original returns an "overview" plus help for
each refinement. If Unit-timer were an object, there'd be no obvious place
for the overview. And I don't know any easy way of getting help for a
function embedded in an object --
>> help watchmaker/start
watchmaker/start is a path
(This seems an obvious design oversight that RT could fix one day).
Objects of course don't just give more room to breathe, they open an
opportunity to work in a quite different way. Your code is a good example of
this in that it assigns the functionality to user variables. I'm not sure
what this next example does to your collection and report-all, but if I want
a timer than lives on between program runs, I can do it with your code:
LongTime: watchmaker/new "long-life"
Longtime/start
...
Longtime/stop
write %longtime.wat mold longtime
......
Longtime: do read %longtime.wat mold
longtime/start
(This would be more useful if the function worked across midnights)
One tiny question. You have a to-seconds function I just use to-decimal. Any
significant difference there?
Thanks again for the rework,
Sunanda.
[4/18] from: fsievert:uos at: 13-Feb-2002 18:04
Maybe you will like my Profiler-Script at REBOL.COM/Sites/FX5, too.
Frank
[5/18] from: greggirwin:mindspring at: 13-Feb-2002 13:30
Hi Sunanda,
<< 02 -- Help Unit-timer in the original returns an "overview" plus help for
each refinement. If Unit-timer were an object, there'd be no obvious place
for the overview. And I don't know any easy way of getting help for a
function embedded in an object -- >>
I've started playing with an idea, just a rough one at this point, to make
it easier to get help for objects. Of course it could grow from there.
The idea is to provide a virtual base object (maybe "template" is a more
REBOLish term). I'm calling the draft version lib-kernel as my first target
is function LIBraries built as objects.
>> my-lib: make lib-kernel [
[ my-prn: :print
[ my-add: func [a b][a + b]
[ ]
>> my-lib/words
== [? help words help-words my-prn my-add]
>> my-lib/help
? [
"Shows help for the object."
/with "Means you want help for one, specific word."
word "The word you want help for."
/local value
]
------------------------------------------------------------------
help [
"Shows help for the object."
/with "Means you want help for one, specific word."
word "The word you want help for."
/local value
]
------------------------------------------------------------------
words []
------------------------------------------------------------------
help-words []
------------------------------------------------------------------
my-prn native
------------------------------------------------------------------
my-add [a b]
------------------------------------------------------------------
>> my-lib/help/with 'my-add
USAGE:
MY-ADD a b
DESCRIPTION:
(undocumented)
ARGUMENTS:
a -- (Type: any)
b -- (Type: any)
;--------------------------------------------
;-- lib-kernel.r source
;--------------------------------------------
REBOL [
Title: "lib-kernel"
Author: "Gregg Irwin"
Email: [greggirwin--acm--org]
Version: 0.0.1
]
lib-kernel: make object! [
;-- This is used to make the help functions in the sub-libraries.
; They can redefine it, of course, but this gives us a base of
; functionality.
?: help: func [
"Shows help for the object."
/with "Means you want help for one, specific word."
word "The word you want help for."
/local value
][
either with [
value: in get 'self :word
either none? :value
[print rejoin [:word " is not a word in library."]]
[system/words/help :value]
][
;?? It might be better to show just the interface for each
; function and/or an overview help text for the object.
foreach word help-words [
; a)
;value: in get 'self :word
;system/words/help :value
; b)
print either function? value: get in get 'self word [
[word mold third :value]
][
[word mold :value]
]
print to-string array/initial 66 "-"
]
]
]
;-- List of words defined in the object.
words: does [next first self]
;-- By default, help will return help for all words in the object.
; You can override this to show help for just a subset of words.
help-words: :words
]
--Gregg
[6/18] from: sunandadh:aol at: 13-Feb-2002 17:04
Hi Gregg
> I've started playing with an idea, just a rough one at this point, to make
> it easier to get help for objects. Of course it could grow from there.
Good luck with that. We need something. Perhaps when the mythical modules
appear in Rebol/3 RT will have done the work for us. But who knows how long
that might be?
Sunanda.
[7/18] from: sunandadh:aol at: 13-Feb-2002 17:03
Hi Frank.
> Maybe you will like my Profiler-Script at REBOL.COM/Sites/FX5, too.
Clever stuff!
Joel and I have both invented methods of instrumenting arbitrary stretches of
code. That's useful to narrow down a problem, once you know the function you
think is being a slug.
Your method is one level up from ours: it can automatically profile all the
functions in an object, thus pinpointing the ones that need closer attention
Sunanda.
[8/18] from: joel:neely:fedex at: 13-Feb-2002 16:46
Hi, Sunanda,
Much good food for thought! I'll digest and reply more later,
but one quick thought ...
[SunandaDH--aol--com] wrote:
> ... And I don't know any easy way of getting help for a
> function embedded in an object --
>
>> foo: make object! [
[ blort: func [
[ {Do something obscure}
[ s [string!] {first arg}
[ n [number!] {second arg}
[ ][
[ n + length? s
[ ]
[ ]
>> gnudge: get in foo 'blort
>> help gnudge
USAGE:
GNUDGE s n
DESCRIPTION:
Do something obscure
GNUDGE is a function value.
ARGUMENTS:
s -- first arg (Type: string)
n -- second arg (Type: number)
>>
That certainly doesn't qualify as "any easy way" but it does
contain a hint for anyone who'd like to tackle writing a
HELP-METHOD function that does for object methods what HELP
does for "ordinary" functions...
-jn-
[9/18] from: brett:codeconscious at: 14-Feb-2002 10:39
Hi,
> > ... And I don't know any easy way of getting help for a
> > function embedded in an object --
DocKimbel's help.r might help you out.
http://rebol.dhs.org/help.r
Regards,
Brett.
[10/18] from: g:santilli:tiscalinet:it at: 14-Feb-2002 16:25
At 14.36 13/02/02, Joel wrote:
Just a small note...
> to-seconds: func [t [time!]] [
> t/hour * 60 + t/minute * 60 + t/second
> ]
>> to-decimal 0:01:05.56
== 65.56
It is already in. :)
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[11/18] from: joel:neely:fedex at: 14-Feb-2002 21:59
Hi, Sunanda,
Things have been a bit too busy for me to follow up before now...
Sorry!
[SunandaDH--aol--com] wrote:
> Object+functions of course give a lot more room to breathe -- it
> is easier to have persistent variables in them than it is with
> function+refinements.
>
I can interpret that statement two ways -- both of which I agree
with wholeheartedly -- and it's quite likely that you meant both
of them, of course!
1) Using "literal" series values inside a function to cache the
persistent data is certainly more obscure and provides more
opportunities for error than the use of object attributes.
2) Using a single object to manage behavior over a single state
(collection of attributes), then MAKEing more objects from
that prototype (if needed for multiple stateful entities) lets
us completely separate the management of the collection from
the management of (each) state.
I've even experimented with building closures via USE but gave up
as soon as I hit the need for multiple "cases" to be active at
once. I couldn't see anything that closures do that objects don't
do at least as well/clearly, and objects are more general.
> From the outside there is almost no difference between calling an
> object/function and a function/refinement. If I rewrote my code
> to be an object but with the same interface, you couldn't tell
> the difference from seeing the invocation:
>
> >> unit-timer/start "A code unit"
>
Perhaps I misunderstood, but I thought part of the original post
expressed a desire to *avoid* having that interface. A simple
typo, such as
unit-timer/start "A code unit"
;
; *LOTS* of lines of code here
;
unit-timer/end "A code unti"
could waste considerable time, thus (I had thought) the desire to
avoid having the string argument at all.
> I say _almost_ no difference for two reasons:
>
> 01 -- In the original code, an invocation without a refinement
> executes the code and returns False:
> >> print unit-timer
> == false
>
Just out of curiousity, why? I saw that, but didn't understand it.
> If it was an object, invocating the object name alone returns
> the object:
> >> print unit-timer
> ?object?
>
> There is no implicit init code for a Rebol object.
>
I'm afraid I don't understand the phrase "init code" in that last
sentence. It is certainly possible to have initialization code
that is evaluated at the time an object is created, but I don't
get the impression that this is what you're talking about.
> 02 -- Help Unit-timer in the original returns an "overview"
> plus help for each refinement. If Unit-timer were an object,
> there'd be no obvious place for the overview. And I don't know
> any easy way of getting help for a function embedded in an object --
>
> >> help watchmaker/start
> watchmaker/start is a path
>
The gimmick I showed in my previous email was certainly a kludge.
I'm glad Brett pulled out the link to DocKimbel's enhanced HELP
(thanks, Brett!). I'd also toyed with the idea of simply having
a HELP method defined within an object (actually, it could even
just be a string!) that provides an overview of the object's
entire protocol.
> (This seems an obvious design oversight that RT could fix one day).
>
Could be. They've certainly accepted such suggestions in the past.
> Objects of course don't just give more room to breathe, they open
> an opportunity to work in a quite different way. Your code is a
<<quoted lines omitted: 10>>
> Longtime: do read %longtime.wat mold
> longtime/start
You could actually save the WATCHMAKER instead of an individual
WATCH. There's only one catch (in the version I posted yesterday)
about restoring the WATCHMAKER from a file, as illustrated below.
>> total-time: watchmaker/new "Total"
>> total-time/start
== 21:31:57.11
>> web-time: watchmaker/new "Web"
>> web-time/start
== 21:32:15.4
>> write %persistentwatchmaker.r mold watchmaker
Time passes... I kept the same session to show that it really did
work to reload the WATCHMAKER (almost).
>> laterwatchmaker: do load %persistentwatchmaker.r
>> laterwatchmaker/collection: reduce laterwatchmaker/collection
== [
make object! [
label: "Total"
total: 0
began: 21:31:57.11
ended: 0
reset: func [][...
>> laterwatchmaker/report-all
== ["Total" 401.18 "Web" 382.89]
>> watchmaker/report-all
== ["Total" 417.88 "Web" 399.59]
>> laterwatchmaker/report-all
== ["Total" 419.96 "Web" 401.67]
I had to REDUCE the collection after reloading... YUCHK!
An easy fix would be to add appropriately-named methods to the
WATCHMAKER, perhaps /CHECKPOINT (and /RESTORE), that would save
(and reinstate) the attributes from each WATCH in the collection
into (and from) a given data file.
> (This would be more useful if the function worked across
> midnights)
>
> One tiny question. You have a to-seconds function I just use
> to-decimal. Any significant difference there?
>
The answers to these (very good) points are that
- I was lazy and in a hurry, and
- I didn't know that TO-DECIMAL on a date! or time! would convert
time-of-day to seconds-since-midnight. (Thanks for that!)
The fix to both points is to revise WATCH as follows:
watch: make object! [
label: ""
total: began: ended: 0
reset: func [] [total: began: 0]
start: func [] [began: now/precise]
stop: func [/local ended] [
ended: now/precise
if date? began [
total: ended - began * 86400 + total +
to-decimal ended/time - began/time
began: 0
total: total + to-seconds ended
]
]
report: func [] [
if date? began [stop start]
reduce [label total]
]
]
Thanks for the suggestions!
-jn-
--
; 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" ;
[12/18] from: sunandadh:aol at: 20-Feb-2002 6:07
Hi Joel,
Thanks for your long and thoughtful reply, I've been offline for most of a
week up in the hills, so I haven't had a chance to comment. Just a few minor
addendums:
> > I say _almost_ no difference for two reasons:
> >
<<quoted lines omitted: 3>>
> > == false
> Just out of curiousity, why? I saw that, but didn't understand it.
Simply because the interface spec says it returns False if you call it
wrongly (e.g. to end an unstarted timer). False for no action specified
looked orthogonal to me.
> > There is no implicit init code for a Rebol object.
> I'm afraid I don't understand the phrase "init code" in that last
> sentence.
You're right and I'm wrong. I don't know what I was thinking about. If you
take this object...
myob: make object! [
var1: 0
var2: 10
print ["object initialized"]
func1: [] [print "func 1 called"]
]
...the two var assignments and the print statement will be executed as part
of the object initialization.
I am right (aren't I?) that there is not implicit (= automatic)
finalization/destructor code in a Rebol object, so my object doesn't get a
last gasp to do something when I code:
unset 'myob
This makes Rebol objects less independant than in some other languages. (I
know I could redefine 'unset, but there are other ways to eliminate an
object).
> You could actually save the WATCHMAKER instead of an individual
> WATCH.
The ability to save and reload objects which contain code is a very powerful
one. So far, I've resisted using it -- the only objects I do save and later
reload are data structures.
I think I'm resistant because I see some horrible maintenance problems. I'd
end up with code snippets spread widely across my database and no obvious way
of changing them all to the latest version.
Sunanda.
[13/18] from: joel:neely:fedex at: 20-Feb-2002 7:14
Hi, Sunanda,
[SunandaDH--aol--com] wrote:
> ... I've been offline for most of a week up in the hills ...
>
Welcome back! I'm jealous! ;-)
> > Just out of curiousity, why? I saw that, but didn't
> > understand it.
>
> Simply because the interface spec says it returns False if you
> call it wrongly (e.g. to end an unstarted timer). False for no
> action specified looked orthogonal to me.
>
Thanks. I must confess that I'm still scratching my head over
the issue of failure and out-of-domain arguments. REBOL can't
seem to make up its mind about "bottom"; one sees all of the
following from various functions:
- NONE
- UNSET!
- ERROR!
and I haven't figured out the decision criteria...
> ... If you take this object...
> myob: make object! [
<<quoted lines omitted: 5>>
> ...the two var assignments and the print statement will be
> executed as part of the object initialization.
But not when one uses MYOB as a prototype, as in
otherob: make myob
> I am right (aren't I?) that there is not implicit (= automatic)
> finalization/destructor code in a Rebol object, so my object
> doesn't get a last gasp to do something when I code:
>
> unset 'myob
>
AFAIK that's correct.
> The ability to save and reload objects which contain code is a
> very powerful one. So far, I've resisted using it -- the only
<<quoted lines omitted: 3>>
> database and no obvious way of changing them all to the latest
> version.
I completely agree. This is a direct consequence IMHO of the
decision to use a prototype model for objects (instead of a
class model). I've wool-gathered a bit about how to apply
delegation model concepts, but haven't satisfied myself with
any thoughts to date WRT to the issues you outlined above.
-jn-
--
; 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" ;
[14/18] from: sunandadh:aol at: 20-Feb-2002 15:19
Joel:
> > ... I've been offline for most of a week up in the hills ...
> Welcome back! I'm jealous! ;-)
I'm being punished for it now by having to reinstall everything on the
machine from the opsys upwards. I guess it got jealous too and trashed itself.
> > ... If you take this object...
> > myob: make object! [
<<quoted lines omitted: 7>>
> > executed as part of the object initialization.
> But not when one uses MYOB as a prototype, as in
otherob: make myob
There's some oddity going on here -- no doubt explained in the core manual
(which I didn't have backed up, and haven't yet redownloaded). The
initialization
code gets executed and then stripped out as part of building
the object. The result of making an object is a series of assignments only,
as this example shows:
myobj1: make object! [
var1: []
print "initialize"
do [append var1 now/precise]
func1: [] [print "func1"]
]
probe myobj1
myobj2: make myobj1 []
probe myobj2
The 'Do and 'Print are executed during
myobj1: make object! [...]
but do not survive into the object itself. So that code does not get executed
in
myobj2: make myobj1 []
That wasn't what I was expecting to see -- so thanks for pointing it out.
There's no obvious workaround that I can see.
Sunanda.
[15/18] from: joel:neely:fedex at: 20-Feb-2002 23:23
My sympathies! :-/
[SunandaDH--aol--com] wrote:
> I'm being punished for it now by having to reinstall everything
> on the machine from the opsys upwards. I guess it got jealous
> too and trashed itself.
>
Been there. Done that. Don't want to go there again.
> There's some oddity going on here -- no doubt explained in the
> core manual (which I didn't have backed up, and haven't yet
<<quoted lines omitted: 19>>
> it out.
> There's no obvious workaround that I can see.
According to http://www.rebol.com/docs/core23/rebolcore-10.html ,
The block is evaluated, so it can include any type of expression
to compute the values of the variables:
example: make object! [
var1: 10
var2: var1 + 10
var3: now/time
]
I tend to think of MAKE OBJECT! [...] as "REDUCE with an attitude",
as shown in the following example:
>> pond: [
[ frog: "Ribbit"
[ duck: "Quack"
[ fish: rejoin [
[ to-string #"S" to-char 112
[ lowercase "L" duck/3
[ "sh"
[ ]
[ ]
== [
frog: "Ribbit"
duck: "Quack"
fish: rejoin [
to-string #"S" to-char 112
lowercase "L" duck/3
...
>> puddle: copy/deep pond
== [
frog: "Ribbit"
duck: "Quack"
fish: rejoin [
to-string #"S" to-char 112
lowercase "L" duck/3
...
Just a block with some set-words and other expressions, until
we do this:
>> wet-place: make object! pond
>> source wet-place
wet-place:
make object! [
frog: "Ribbit"
duck: "Quack"
fish: "Splash"
]
All of the set-words at the top level of POND have been changed
to belong to a new context.
>> frog: "Croak"
== "Croak"
(just for verification).
>> get pond/1
== "Ribbit"
>> get pond/3
== "Quack"
>> get pond/5
== "Splash"
Contrasted with
>> get puddle/1
== "Croak"
>> get puddle/3
** Script Error: duck has no value
** Near: get puddle/3
>> get puddle/5
** Script Error: fish has no value
** Near: get puddle/5
And, for another contrast
>> reduce pond
== ["Ribbit" "Quack" "Splash"]
versus
>> reduce [frog: "Kneedep" duck: "Honk" fish: "Blubb"]
== ["Kneedep" "Honk" "Blubb"]
>> get puddle/1
== "Kneedep"
>> get puddle/3
== "Honk"
>> get puddle/5
== "Blubb"
to prove that the words in POND are no longer in the global
context.
However, since the spec-block was essentially reduced to
establish the values at the time the object/context was
created, the expressions don't exist any more (as expressions)
in the object itself.
Now that I've tried to verbalize it, I'm not so sure that
the words capture the idea, which seems so simple. Hmmmm.
Maybe English is a foreign language to programmers!
-jn-
--
; 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" ;
[16/18] from: g:santilli:tiscalinet:it at: 21-Feb-2002 0:05
At 21.19 20/02/02, Sunanda wrote:
>There's some oddity going on here -- no doubt explained in the core manual
>(which I didn't have backed up, and haven't yet redownloaded). The
Well, I think users don't expect this behavior because they are used to the
term "object" as used in OOP (and in particular in class-based OOP). Not to
go against Joel, ;) but if RT actually named OBJECT! as CONTEXT! instead
this confusion would not exist. (They probably didn't just because they have
a CONTEXT! already internally and because OBJECT!s can also be used for OO
programming in REBOL.)
A REBOL OBJECT! is no more than a series of name/value pairs, and that what
is usually referred ad "a context" in REBOL. Things are just simpler than
what most users expect. :)
Regards,
Gabriele.
--
Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer
Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r
[17/18] from: joel:neely:fedex at: 21-Feb-2002 9:45
Hi, again, Gabriele,
Gabriele Santilli wrote:
> Well, I think users don't expect this behavior because they are
> used to the term "object" as used in OOP (and in particular in
> class-based OOP).
>
I'd say that most people ONLY know the term "object" in terms of
the class-based OO model. People (the minority) who know about
other options for "objects", such as prototype-based or delegation-
based models, won't have the problem you're addressing. There
has been significant work in a variety of ways of understanding
objects and how to use and implement them.
Unfortunately, the (numerical) predominance of c and c++ has led
most people to assume that dynamic data structures require pointers
and objects require classes.
> Not to go against Joel, ;) but if RT actually named OBJECT! as
> CONTEXT! instead this confusion would not exist...
>
By "this confusion" I assume you mean the issue of initialization
that Sunanda raised. (The "other confusion" I've addressed in a
separate post. ;-)
It seems to me that RT had two options:
1) Give things completely different names, to avoid leading people
to think they understand things and bring in irrelevant baggage.
2) Give things conventional-sounding names, to help people make a
connection with what they already know.
This choice is not specific to REBOL. I've heard it claimed that the
designers of Java deliberately chose to make it resemble c++ rather
than Smalltalk was to avoid scaring off people whose understanding of
programming was limited to c/c++. That strategy appears to have
worked for them!
But choosing option #2 implies the need for clear explanations and
documentation to help people understand *how* things are similar to,
and different from, what they already know.
-jn-
[18/18] from: sunandadh:aol at: 21-Feb-2002 11:16
Hi Gabriele,
> Things are just simpler than
> what most users expect. :)
Thanks for the explanation. It's a bit like life itself, isn't it? As a great
person once said "Life _is_ complicated. But if it was any less complicated,
we'd all be too simple to understand it".
Sunanda.
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted