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

To do or not to do?

 [1/13] from: sanghabum:aol at: 28-Nov-2001 15:40


Hi all, It may just be my irredeemably old-fashioned mindset, but everywhere I turn in Rebol I see the need to 'DO strings. I've been ticked off before on the list about it and -- these days -- I can normally see better approaches. But this little example has got me stumped. So I'm interested in how the gurus would unDo my approach. The code below is the proof-of-concept, back of the envelope idea that many data entry validation rules can be written to a database as Rebol code. I just then need to write a single apply-the-rules function, and add a little code for special cases, rather than write buckets of repetitive code. But its got a DO string in it. Any comments? --Colin ---------------------------------- ;; rules table ;; =========== Rules: [ "(Length? to-string *) > 0" "No data" "date? *" "Bad date" "(* - 14 ) < now/date" "Too old" "specialcheck * " "Not special" ] ;; prepare data field ;; ============== RawValue: "5-122-2001" ;; bad date in this example Loadedvalue: "" if error? try [loadedValue: first load/all Rawvalue] [LoadedValue: RawValue] ;; apply rules ;; =========== foreach [rule message] Rules [ if not (do replace/all copy rule "*" mold LoadedValue) [ print [LoadedValue " fails rule: " Message] break ] ; if ] ; for

 [2/13] from: sterling:rebol at: 28-Nov-2001 14:06


NOOOOOOOOOO!!!! There is always a way not to do strings. :) Think about it this way... what are you building in your string that can be done? Answer: REBOL code. Why does this work? type? 5-Nov-2001 or this? type? first [10] Answer: because REBOL understands REBOL. Now what's the difference between these two statements? a: "print 10" b: [print 10] Not much. do a 10 do b 10 Great. So why do we want to use the second one? Well, check this out. type? second a char! type? second b integer! How about this? find a integer! == none find b integer! [10] This is useful stuff! You can change your code far easier this way than with strings. REBOL knows what you're dealing with and can help you out! Now, the super-quick translation of your code below. I re-arranged the blocks so that the replacement would be simple but you could just as easily do it using COMPOSE or BIND to get it done. I use the word 'item in the block to be the replacement instead of "*" since words are meant to be symbols and that's exactly what we need. Rules: [ [0 < length? to-string item] "No data" [date? item] "Bad date" [greater? now/date item - 14] "Too old" [specialcheck item] "Not special" ] ;; prepare data field ;; ============== RawValue: "5-12-2001" ;; bad date in this example Loadedvalue: "" if error? try [loadedValue: first load/all Rawvalue] [LoadedValue: RawValue] ;; apply rules ;; =========== foreach [rule message] Rules [ if not (do replace/all copy rule 'item LoadedValue) [ print [LoadedValue " fails rule: " Message] break ] ; if ] ; for I'm using a valid date here so it gets through more of the checks eventually failing because specialcheck is not defined. I hope this has helped you out. If you have any more questions about it, please ask... we all cringe here at REBOL HQ when we see the unnecessary use of DO with strings... you're always DOing REBOL code so why not start that way? Sterling

 [3/13] from: rotenca:telvia:it at: 28-Nov-2001 23:40


Hi, the first thing i've done is (but there are others methods): ;; rules table ;; =========== Rules: [ [(length? to-string x) > 0] "No data" [date? x] "Bad date" [(x - 14 ) < now/date] "Too old" [specialcheck x] "Not special" ] ;; prepare data field ;; ============== RawValue: "5-122-2001" ;; bad date in this example Loadedvalue: "" if error? try [loadedValue: first load/all Rawvalue] [LoadedValue: RawValue] ;; apply rules ;; =========== foreach [rule message] Rules [ if not do func [x] rule LoadedValue [ print [LoadedValue " fails rule: " Message] break ] ; if ] ; for --- Ciao Romano

 [4/13] from: joel:neely:fedex at: 28-Nov-2001 16:53


Hi, Colin, No guru here, JARH... [Sanghabum--aol--com] wrote:
> But this little example has got me stumped. So I'm interested > in how the gurus would unDo my approach. >
Let each rule be an anonymous function, applied to the value being considered.
> ---------------------------------- > ;; rules table
<<quoted lines omitted: 19>>
> ] ; for > ----------------------------------
As follows: ;; "special" check ;-) ;; specialcheck: func [x] [ random true ] ;; rules table ;; =========== Rules: reduce [ func [x] [0 < Length? to-string x] "No data" func [x] [date? x] "Bad date" func [x] [x - 14 < now/date] "Too old" func [x] [specialcheck x] "Not special" ] ;; examine data field ;; ================== testTheField: function [ RawValue [string!] ][ LoadedValue ][ if error? try [ LoadedValue: first load/all RawValue ][ LoadedValue: RawValue ] ;; apply rules ;; =========== foreach [rule message] Rules [ if not (rule LoadedValue) [ print [LoadedValue " fails rule: " message] return false ] ; if ] ; for true ] which performs as follows:
>> testTheField "5-122-2001"
5-122-2001 fails rule: Bad date == false
>> testTheField "11-01-2001"
11-Jan-2001 fails rule: Not special == false
>> testTheField "11-28-2001"
11-28-2001 fails rule: Bad date == false
>> testTheField "11-01-2001"
11-Jan-2001 fails rule: Not special == false
>> testTheField "11-28-2001"
11-28-2001 fails rule: Bad date == false
>> testTheField "11-01-2001"
== true
>>
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" ;

 [5/13] from: sanghabum:aol at: 28-Nov-2001 19:23


Thanks to Romano, Sterling, and Joel for the instant enlightenment Sterling writes:
> NOOOOOOOOOO!!!! > There is always a way not to do strings. :) <snip> > I hope this has helped you out. If you have any more questions about > it, please ask... we all cringe here at REBOL HQ when we see the > unnecessary use of DO with strings..
Let me show you the depths of my ignorance, which might help or inspire someone to write a best-downloading how-to on Blocks and their uses..... I hate to see a grown man cringe, so I'll close my eyes as I write the next sentence..... I almost always build a 'Layout as a string. Why? Because building one with blocks seems to require lots of special tricks. There's a synthetic example below what I mean. It shows in miniature the sort of things that happen if a 'Layout is made up of loads of components sourced from different places (user color choices from one file, screen metric calculations from elsewhere, list of fields needed from a CSV file, etc). As a string, I can do it in moments, and get on with my life. Blocks just seem too darned difficult in this instance. But, as I said last time, maybe it's not Rebol. Maybe it's my brain that's at fault. Colin Rebol [] ========================================= ;; layout we want to achieve: ;; ======================== Wanted: [across b: box 99x100 green Return c: box 45x33 green + 150.150.150 zz: text "last field" ] ;; Various bits of derived data needed ;; =================================== MaxBoxWidth: 99 MaxBoxdepth: 100 BoxColor: "Green" LastField: "zz" ;; A function to do it with strings ;; ================================ makelayout: func [/local lay] [ lay: join "across " [ " b: box " to-pair (MaxBoxWidth MaxBoxDepth) " " boxcolor " return " " c: box " to-pair (to-integer MaxBoxWidth / 2 to-integer MaxBoxDepth / 3) " " boxcolor " + 150.150.150 " lastfield ": text {last field}" ] return lay ] ;; go do it ;; ======== unview/all view layout load MakeLayout

 [6/13] from: rotenca:telvia:it at: 29-Nov-2001 2:39


Hi, things are more simple of what you think. I have only removed the string and the function and (almost) everything works as you want. The only differences are: 1) a: 300 b: 1 to-pair (a b) gives 1x1 because (a b) is = 1 try it on the console (the best friend of rebol programmer) a right mode is: to-pair reduce [a b] 2) Another problem is that zz: will be a global word; to-set-word changes the binding of the word and transform it in a global word. If zz: must be local the code requires some little changes. 3) you can use compose or a combination of insert/append ;; layout we want to achieve: ;; ======================== Wanted: [across b: box 99x100 green Return c: box 45x33 green + 150.150.150 zz: text "last field" ] ;; Various bits of derived data needed ;; =================================== MaxBoxWidth: 99 MaxBoxdepth: 100 BoxColor: Green LastField: 'zz ;; A function to do it with strings ;; ================================ ; a way with compose lay: compose [ across b: box to-pair reduce [MaxBoxWidth MaxBoxDepth] boxcolor return c: box to-pair reduce [to-integer MaxBoxWidth / 2 to-integer MaxBoxDepth / 3] boxcolor + 150.150.150 (to-set-word :lastfield) text {last field} ] ;another way with insert/append lay: append [ across b: box to-pair reduce [MaxBoxWidth MaxBoxDepth] boxcolor return c: box to-pair reduce [to-integer MaxBoxWidth / 2 to-integer MaxBoxDepth / 3] boxcolor + 150.150.150 ] head insert [text {last field}] to-set-word :lastfield ;; go do it ;; ======== unview/all view x: layout lay --- Ciao Romano

 [7/13] from: al::bri::xtra::co::nz at: 29-Nov-2001 20:41


Colin wrote:
> Rules: [ > "(Length? to-string *) > 0" "No data" > "date? *" "Bad date" > "(* - 14 ) < now/date" "Too old" > "specialcheck * " "Not special" > ]
Better would be: Rules: [ [(Length? to-string Value) > 0] "No data" [date? Value] "Bad date" [(Value - 14) < now/date] "Too old" [specialcheck Value] "Not special" ] And then instead of:
> foreach [rule message] Rules [ > if not (do replace/all copy rule "*" mold LoadedValue) [ > print [LoadedValue " fails rule: " Message] > break > ] ; if > ] ; for
try something like: foreach [Rule Message] Rules [ Value: LoadedValue if not do bind Rule 'Value [ print [Value "fails rule:" Message] break ] ] This way you avoid Rebol having to repeatedly parse the string. Instead you work directly with Rebol values, and avoid having to use special syntax in your rules. I hope that helps! Andrew Martin Who wishes Rebol could play DVD movies... ICQ: 26227169 http://valley.150m.com/

 [8/13] from: lmecir:mbox:vol:cz at: 29-Nov-2001 9:20


Hi, <<Romano>> (...) Another problem is that zz: will be a global word; to-set-word changes the binding of the word and transform it in a global word. If zz: must be local the code requires some little changes. (...) <</Romano>> Although my Convert-word function (can be found in http://www.sweb.cz/LMecir/contexts.html ) does preserve the context when it converts a word to another datatype, I would prefer a refinement for the To function like (to/context set-word! something). -Ladislav

 [9/13] from: sanghabum:aol at: 29-Nov-2001 5:30


Andrew Martin writes:
> This way you avoid Rebol having to repeatedly parse the string. Instead you > work directly with Rebol values, and avoid having to use special syntax in > your rules. > > I hope that helps! >
It does indeed. Thanks again to everyone for the education and training, Colin.

 [10/13] from: sanghabum:aol at: 29-Nov-2001 5:30


Thanks again Romano,
> things are more simple of what you think. I have only removed the string and > the function and (almost) everything works as you want. > The only differences are: <snip>
I appreciate the effort you and all the others have taken to show me the error of my ways. I've reworked your code to fit my "real world" -- both Boxcolor and Lastfield musy be strings -- they are sourced from a spreadsheet. And it works with Compose. I dunno, this sort of thing is a matter of taste as much as anything, but I reckon the original "makelayout" is an easier bit of code to follow and amend than the version below. The compose version needs several Rebol tricks (to-set-word, get to-word) that the (perhaps brain-dead and inelegant) string 'em up original doesn't. (And I guess when I write code, the target audience I have in mind includes the maintenance programmer in 5 years time who is not an expert in either the system or the languages used but, nonetheless has to make modifications fast.) Thanks all again, Colin ========================================= Wanted: [across b: box 99x100 green Return c: box 45x33 green + 150.150.150 zz: text "last field" ] ;; Various bits of derived data needed ;; =================================== MaxBoxWidth: 99 MaxBoxdepth: 100 BoxColor: "Green" LastField: "zz" ; a way with compose givem BoxColor is a String ;; ============================================ MakeLayout: func [/local lay] [ lay: compose [ across b: box to-pair reduce [MaxBoxWidth MaxBoxDepth] (get to-word boxcolor) return c: box to-pair reduce [to-integer MaxBoxWidth / 2 to-integer MaxBoxDepth / 3] ((get to-word boxcolor) + 150.150.150) (to-set-word :lastfield) text "last field" ] return lay ] ;; go do it ;; ======== unview/all view layout makelayout

 [11/13] from: rotenca:telvia:it at: 29-Nov-2001 17:22


Hi Ladislav,
> Although my Convert-word function (can be found in > http://www.sweb.cz/LMecir/contexts.html ) does preserve the context when it > converts a word to another datatype, I would prefer a refinement for the To
I know. The limit is bind which does not bind to any but word! datatype! I do not understand why RT have not made bind more general (any-word!) If the starting word was [:z] noone can convert it to set-word/word/lit-word while preserving binding. It is a bad hole in Rebol.
> function like (to/context set-word! something).
What i do not understand is why 'to does not preserve binding. I understand that some values passed to to-word have not an original binding, like string, and that only the global context can be extended. But i think that is difficult to remember that code like this: a: 50 use [a] [ a: 3 print :a print get to-get-word 'a ] gives as result 3 50 It can generate subtle and difficult-to-catch error. Your /context should return an error if the word is not already defined in the context - this should be true also for global context. For ortogonality and block contexts (use/func) i think that the sintax should be: to/context set-word! 'word-in-a-context like with bind (and an implicit bind is exactly what we want). --- Ciao Romano

 [12/13] from: rotenca:telvia:it at: 29-Nov-2001 18:58


Hi,
> I dunno, this sort of thing is a matter of taste as much as > anything,
Yes, but it is almost like the difference between compiled / not compiled code. A string is like source code for Rebol. It must be compiled (loaded) and then run (interpreted). A block is like compiled code: it must only be run (interpreted). If the overhead is not a problem, you can compile your C code every time you run it :-) (it is not just the same thing, I must admit :-()
>but I reckon the original "makelayout" is an easier > bit of code to follow and amend than the version below.
I disagree: 1) now your code is more readable than before (from a Rebol point of view) 2) now a reader can understand better which are the conversions you operate But the code could be more clear if you separe the conversions from the executing: BoxColor: "Green" LastField: "zz" ... BoxColorW: get to-word BoxColor ; or BoxColorW: get load BoxColor LastFieldW: to-set-word LastField ; or LastFieldW: to-set-word load LastField The 'load func add an additional "internal check" (which is long to explain) to the conversion of the string which both to-word and to-set-word escape. Here you can check conversion errors and then use a layout block with compose only for the set-word. MakeLayout: does [ ;this name seems to me wrong, the func returns only a block! compose [ across b: box to-pair reduce [MaxBoxWidth MaxBoxDepth] boxcolorw return c: box to-pair reduce [ to-integer MaxBoxWidth / 2 to-integer MaxBoxDepth / 3 ] boxcolorw + 150.150.150 (:lastfieldw) text "last field" ] ]
> The > "compose" version needs several Rebol tricks (to-set-word, > get to-word) that the (perhaps brain-dead and inelegant) > "string 'em up" original doesn't.
It made it in an implicit mode. But it made many others not necessary conversions of your string-code.
> (And I guess when I write code, the target audience I have in > mind includes the maintenance programmer in 5 years time who > is not an expert in either the system or the languages used > but, nonetheless has to make modifications fast.)
In 5 years Rebol block will be the standard way to write code for beginners :-) To end, in your code: b: box to-pair reduce [MaxBoxWidth MaxBoxDepth] (get to-word boxcolor) ... ((get to-word boxcolor) + 150.150.150) can be b: box to-pair reduce [MaxBoxWidth MaxBoxDepth] (to-word boxcolor) ... (to-word boxcolor) + 150.150.150
> Colin
--- Ciao Romano

 [13/13] from: sanghabum:aol at: 29-Nov-2001 19:06


Hi Romano, Thanks for the detailed response...
> A string is like source code for Rebol. It must be compiled (loaded) and > then > run (interpreted). A block is like compiled code: it must only be run > (interpreted). > If the overhead is not a problem, you can compile your C code every time
you
> run it :-) (it is not just the same thing, I must admit :-()
In the case of my code generating Layouts, that's a once-a-run issue, so I suspect the time to compose-and-do is similar to string-and-do. But for the example that started this thread (validation rules on a database), your distinction makes it clear that a block is best where run time is an issue.
> In 5 years Rebol block will be the standard way to write code for beginners > :-)
Wouldn't that be great!?
> To end, in your code:
b: box to-pair reduce [MaxBoxWidth MaxBoxDepth] (get to-word boxcolor)
> ... > ((get to-word boxcolor) + 150.150.150) > can be > b: box to-pair reduce [MaxBoxWidth MaxBoxDepth] (to-word boxcolor) > ... > (to-word boxcolor) + 150.150.150
This is one of the gotchas in testing line-by-line from the console. Your simplification doesn't work from the console:
>> (to-word boxcolor) + 150.150.150
** Script Error: Cannot use add on word! value ** Near: (to-word boxcolor) + 150.150.150 But it does work in the Compose+Layout combination. It's been a useful training exercise working out why. In homage to Perl's slogan TMTOWTDI ("There's more than one way to do it") maybe we need a rebol riposte: TAMMEWTDI.BIWBETFITAD.JAOTRL). ("There's a much more elegant way to do it. But it won't be easy to find in the available documentation. Just ask on the Rebol list") <g>Colin.

Notes
  • Quoted lines have been omitted from some messages.
    View the message alone to see the lines that have been omitted