Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

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