About Standards and Best Practices
[1/11] from: reboler::bol::com::br at: 1-Sep-2003 7:07
Hi Folks!, Sorry if it's a dummy question, but I was wondering about saving system resources in Rebol. What do you suggest as the best practices? 1. Always use objects? 2. Functions encapsulated in Objects with local vars ? 3. use clear and unset to release the vars? Or I don't need to create the objects and what I need is just to create the functions with local vars to save memory, then clear them.. Have you already written about this? I mean with the focus on Rebol. I think if we have something like this, we would have more improvement to coding. I read a message of Andrew optimizing his code, I think a manual how to optimize a script with standards and best practices would be great!. Don't you think? Best Regards Thanks in advance. -Dj
[2/11] from: greggirwin:mindspring at: 1-Sep-2003 17:37
Hi Dj, R> Sorry if it's a dummy question, but I was wondering about saving system R> resources in Rebol. R> What do you suggest as the best practices? R> 1. Always use objects? R> 2. Functions encapsulated in Objects with local vars ? R> 3. use clear and unset to release the vars? R> Or I don't need to create the objects and what I need is just to create the R> functions with local vars to save memory, then clear them.. It's not a dumb question, but I haven't seen too much discussion about it either. The only time really severe resource usage occurs is when you have *lots* of data (e.g. tens of thousands of items in blocks) or when you have a View app that creates a large number of faces dynamically. One of the nice things about REBOL is really *not* worrying about those details most of the time. :) Do you have a specific situation you're dealing with? -- Gregg
[3/11] from: reboler:bol at: 1-Sep-2003 22:54
Hi Gregg, I know that Rebol is cool!!, but what I was thinking is that any programmer has his view of Rebol. Some of them use a lot of objects, others use just function calls. By the other hand, Rebol is so handy that we can also use a procedural approach. But what are you missing here? the performance!! In my point of view, we can make the script to run. Then, make it run faster and faster!!. Do you know what I mean ? I saw a lot of documentation on style scripting, functions and commands, but not for optimization such as: global vars, local vars, encapsulation, substitution of functions by other that can handle better than another.. etc. We have this information disperse on the list. In most of times, We need to evaluate other's coding to learn with the Gurus.. do you know what I mean ? What I'm asking is just a few tips such as: 1) create objects when .... 2) always use local vars inside functions. (or if this var is inside a function, it's a local var anyway). 3) call various scripts or code everything in one file.. 4) clear, reset vars.. 5) use dynamic vars inside functions.. 6) work creating instances of objects I know, it's very dummy, but it helps people.. you know. Best Regards Thans in advance --DJ
[4/11] from: brett:codeconscious at: 2-Sep-2003 12:38
> What I'm asking is just a few tips such as: > 1) create objects when ....
<<quoted lines omitted: 4>>> 5) use dynamic vars inside functions.. > 6) work creating instances of objects
Very good question. I have struggled with coming up with my own style that takes most advantage of REBOL's flexibility. The thing that I have found is that REBOL's flexibility actually makes it hard to create rules like "Always ....". I invariably find another situation that makes the very thing I'm trying to avoid useful or innovative. So it turns on the situation. It is probably best then to have a place where these "idioms" can be proposed and discussed. Each should establish a situation and a possible idiom that would be useful. Like another cookbook - but aimed at someone who has already learnt the basics of REBOL. This is similar in concept to the early days of "design patterns" - but I would suggest trying to avoid the dogmatism that I saw in that area of work when I last looked. The risk is if you go into too much detail (it must be this way or that) then you lose the value of the idea, I think. The existing REBOL code produced by RT is implicitly a guide too. Regards, Brett
[5/11] from: andrew:martin:colenso:school at: 2-Sep-2003 14:56
> What I'm asking is just a few tips such as: > 1) create objects when ....
Use objects when the names in the script look something like this: Bla_foo: ; Rebol value here. Bla_bar: ; Rebol value here. Bla_thing: ; Rebol value here. Turn the above into: Bla: make object! [ foo: ; Rebol value here. bar: ; Rebol value here. thing: ; Rebol value here. ] Why? An object keeps things together that work together and makes it obvious.
> 3) call various scripts or code everything in one file...
Or the third option, put one's common functions into files of their own making a library of files/script/values, then assemble them automatically into a larger script/file for distribution. Why? Minimises redundancy, and there's only source. If a mistake is found in a library file, one only needs to correct it one place, then reassemble any distributions that rely on that script/library (or even just re-assemble them all if not lazy). Just my opinion. Andrew J Martin Attendance Officer & Information Systems Trouble Shooter Colenso High School Arnold Street, Napier. Tel: 64-6-8310180 ext 826 Fax: 64-6-8336759 http://colenso.net/scripts/Wiki.r?AJM http://www.colenso.school.nz/ DISCLAIMER: Colenso High School and its Board of Trustees is not responsible (or legally liable) for materials distributed to or acquired from user e-mail accounts. You can report any misuse of an e-mail account to our ICT Manager and the complaint will be investigated. (Misuse can come in many forms, but can be viewed as any material sent/received that indicate or suggest pornography, unethical or illegal solicitation, racism, sexism, inappropriate language and/or other issues described in our Acceptable Use Policy.) All outgoing messages are certified virus-free by McAfee GroupShield Exchange 5.10.285.0 Phone: +64 6 843 5095 or Fax: +64 6 833 6759 or E-mail: [postmaster--colenso--school--nz]
[6/11] from: greggirwin:mindspring at: 2-Sep-2003 9:18
Hi Reboler, R> I know that Rebol is cool!!, but what I was thinking is that any programmer R> has his view of Rebol. Some of them use a lot of objects, others use just R> function calls. By the other hand, Rebol is so handy that we can also use a R> procedural approach. But what are you missing here? the performance!! Your questions aren't dumb at all, just hard to answer. :) The problem is that, with REBOL, you can do things in so many different ways that it's hard to define "general" optimizations, as Brett said. Sometimes rethinking the solution in an entirely different way--maybe not possible in other languages--will yield a simpler and much more efficient implementation. That said, there are some obvious, and not-so-obvious rules you can follow. 1) Premature optimization is a big problem is software development. Only optimize things you *know* will make a significant difference. To find those things, you have to profile and measure. I profile things in different ways, but here's a little function I use to test isolated blocks of code. time-it: func [block /count ct /local t] [ if not count [ct: 1] t: now/time/precise loop ct [do block] now/time/precise - t ] print time-it  1'000'000 i: 0 print time-it [i: i + 1] 1'000'000 fn: func [/ref val] print time-it/count [fn] 1'000'000 print time-it/count [fn/ref none] 1'000'000 print time-it/count [make object! ] 1'000'000 print time-it/count [ make object! [a: none b: "test" c: copy ] ] 1'000'000 2) Optimize algorithms before you optimize code. In REBOL, we're already working at a high level, but this can also mean using the right data structure or datatype. For example, list! values are faster than blocks if you are inserting and removing a lot of items, and hash! values are much faster than blocks if you're doing a lot of lookups (i.e. finding or selecting items). 3) Function calls take time. You will sometimes see people inline the source of the APPEND function (which is basically INSERT TAIL) to save a little time. If you have a loop that will make lots of calls, it can *sometimes* be worth eliminating function calls this way, but remember that it can also lead to more bugs and more code. 4) Path evaluation takes time. REBOL is interpreted, so sometimes it can pay to cache values that are part of a deep path (in nested blocks for example) if you're going to use them many times inside a loop. This applies to functions inside objects as well. 5) For dynamic code, build blocks rather than strings if you're going to DO them. It can be much easier, cleaner, and more efficient. 6) Use natives when you can. REBOL's natives are very fast. Rather than looping over a series yourself, looking for values, try to figure out a way to use natives like FIND and SELECT. Let REBOL do all the heavy lifting when you can. 6.1) Get to know how some mezzanines work and which functions are native. The FOR function is a good example. I see a lot of code that uses FOR with a lower bound of 1 and a step size of 1. In those cases, REPEAT is much more efficient. Loop constructs in REBOL are not all native, and certainly not all equal in how they work, so they're worth investigating. 7) If you're processing text, don't forget about PARSE (even if you think it's not applicable to your situation). R> 4) clear, reset vars.. This can make a substantial difference in some cases. If you're creating buffers to use over and over again, particularly large ones, rather than doing a "make string! 1'000'000" on every pass, do it once and then just CLEAR the string on each pass. For me, the big gains come when I can refactor a solution. Often times stripping out my own code when I find a native alternative will be all it takes. HTH! -- Gregg
[7/11] from: rebol:techscribe at: 2-Sep-2003 9:06
To flesh out Andrew's reply a little: 1. Use objects if you want to process assemblies of values (records) that share the same structure uniformly, even though at times some of the values may be missing. The prototype object ensures that all data instances will have a complete set of fields that are initialized with some useful value (in our example we'll be generating reports, and therefore if a record is missing some data, we will want to display N/A in our report). Now, even though our records are deficient (see the two records examples below), they can be treated uniformly, i.e. without implementing exception handling for missing values: Example: person!: make object! [ first-name: "N/A" last-name: "N/A" phone: "N/A" email: "N/A" website: "N/A" address: "N/A" ] persons: make block! 1000 ;- deficient record: no email address or web site append persons make person! [ first-name: "Tom" last-name: "Smith" phone: "555-555-5555" address: "Main Street, Ukiah, CA 95482" ] ;- deficient record: no phone number or web site append persons make person! [ first-name: "Bill" last-name: "Jones" address: "Main Street, Ukiah, CA 95482" email: "[bjones--earthlink--net]" ] ;- generic display function display-list: func [field] [ foreach person persons [ print [ person/first-name person/last-name get in person field ] ] ] list-phone-numbers: does [ display-list 'phone ] list-email-addresses: does [ display-list 'email ] list-addresses: does [ display-list 'address ] list-phone-numbers list-email-addresses list-addresses 2. Use objects, when you have hierarchies of structures: animal!: make object! [ species: "N/A" extinct: "N/A"] dinosaur: make animal! [ species: "Mamal" extinct: "Yes" lived-from: 1965 lived-to: 1979 ] ;- must lookup those numbers! cat: make animal! [ species: "Mamal" extinct: "No" likes: "Milk, Mice, Fireplace" ] 3. Use objects to subcatgorize data: contact!: make object! [ name: "N/A" first-name: "N/A" phone: "N/A" address: "N/A" email: "N/A" website: "N/A" ] company!: make object! [ office: make contact!  contact: make contact!  ] rebol-tech: make company! [ office: make contact! [ ....] contact: make contact! [ .... ] ] That will enable you to look up office related data as rebol-tech/office/... and contact related data as rebol-tech/contact/... with both subcategories having the same structure, i.e. you can again implement a single set of functions to process both types of data. Andrew Martin wrote:
[8/11] from: atruter:labyrinth:au at: 3-Sep-2003 10:29
Hi Gregg, I think your points plus Elan's object observations could almost be placed verbatim in the Cookbook! I'm glad that you could verbalise what many on this list (including me finally) do at an intuitive level. While I have no difficulty explaining [to non-REBOL folks] *what* my REBOL code does, I have often found it difficult to explain how and why the final solution is expreseed the way it is. I think you just made that task a bit easier! ;) Regards, Ashley
[9/11] from: greggirwin:mindspring at: 2-Sep-2003 20:28
Hi Ashley, AT> ...I have often found it difficult to explain how and why the final solution is AT> expreseed the way it is. I think you just made that task a bit easier! ;) Thanks for the kinds words. :) -- Gregg
[10/11] from: rebol:techscribe at: 3-Sep-2003 9:25
Hi. Another issue that comes to mind is name space and side effects. If a function introduces a word that will be used within the context of that function only, then that word should be declared local to the function in order to avoid name clashes that may result in unexpected side effects: f: func [ /local word] [ word: "function's local word." ] This prevents errors that result from setting a global word to some value, then evaluating a function that happens to set the same word locally to a different value, and then continuing to use the global word. This is especially critical if functions are reused either by their original author or by some unsuspecting consumer. Example: ;--- consumer's script REBOL  ;- globally declared word erase-harddrive-completely?: NO ;- evaluating function from a downloaded library do %down-loaded-library ;- contains function list-directory this-dir-contents: list-directory . . . if erase-harddrive-completely? [ erase-harddrive-completely ] ;--- end user's script So, what's the problem? Imagine the function "borrowed" from the downloaded library looks like this: ;--- function in downloaded library -- list-directory: func [ /interactive ] [ erase-harddrive-completely?: "No" directory-listing: read %. if interactive [ display-directory-listing erase-harddrive-completely?: ask "Should I erase the hard drive completely?" ] directory-listing ];---- end libraray function ---- Note that the user's script sets erase-harddrive-completely? to the logic! value No, whereas the function sets the word erase-harddrive-completely? to the string! "No". The string! "No", however, evaluates as a logic! True:
>> a: No
>> if a [ print "yes" ]
>> a: "No"
>> if a [ print "yes" ]
Thus, the harddrive will be erased, even though the library function consumer smartly set the erase-harddrive-completely? word to No. If, however, the library function provider had used a /local word in his function, then it would have not had any affect on the global word. Take Care, Elan
[11/11] from: reboler:bol at: 3-Sep-2003 17:31
Hi Folks!, Thanks to all of you. Elan, Greg, Ashley and Andrew. These are real good tips that makes me change the way I was programming. Other things I think should be interesting is the benchmark of functions.. a) conditional loops.. 'repeat instead of 'foreach, 'forall instead of 'foreach.; b) parse or load/markup (if using parse is faster than 'load/markup. You know, things like that. ok. Thank you again. Best regards, -DJ ----- Original Message ----- From: "Elan" <[rebol--techscribe--com]> To: <[rebol-list--rebol--com]> Sent: Wednesday, September 03, 2003 1:25 PM Subject: [REBOL] Re: About Standards and Best Practices
> Hi. > Another issue that comes to mind is name space and side effects. If a
<<quoted lines omitted: 5>>> ] > This prevents errors that result from setting a global word to some value,
then evaluating a function that happens to set the same word locally to a different value, and then continuing to use the global word. This is especially critical if functions are reused either by their original author or by some unsuspecting consumer. Example:
> ;--- consumer's script > REBOL 
<<quoted lines omitted: 9>>> ;--- end user's script > So, what's the problem? Imagine the function "borrowed" from the
downloaded library looks like this:
> ;--- function in downloaded library -- > list-directory: func [ /interactive ] [
<<quoted lines omitted: 3>>> display-directory-listing > erase-harddrive-completely?: ask "Should I erase the hard drive
> ] > directory-listing > ];---- end libraray function ---- > > Note that the user's script sets erase-harddrive-completely? to the logic!
value No, whereas the function sets the word erase-harddrive-completely? to the string! "No". The string! "No", however, evaluates as a logic! True:
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted