From: Trevor D. (Twylite) <tw...@cr...> - 2012-08-15 14:35:09
|
Hi, On 2012/08/15 03:27 PM, Donal K. Fellows wrote: > On 15/08/2012 11:53, Trevor Davel (Twylite) wrote: >> % after 1000 [string map [list @VAL [list $value]] { puts "Prefix: >> @VAL" }] > The code smell out of that convoluted combination is really large! > Luckily, we can use [apply] to do this much more pleasantly (assuming > your first two lines): Context is your friend ;) I wasn't suggesting this was a good solution, I was illustrating the problem with the construction (i.e. possible unintentional code execution) that is easily missed. If I recall correctly we had used that particular approach in an 8.4 interp, before 8.5 or [apply] were available. And while there was almost certainly a pure-Tcl [apply] on the Wiki, I didn't know about it back then -- there was plenty of example code in those days that recommended [subst] or [string map] for dynamic script generation. I still encounter developers - typically from a C or Java background - who are are unfamiliar and uncomfortable with anonymous procs, and will try the dynamic script generation route first. You'll also note in the conversation history (below) that we did end up with [apply] as the solution: On 2012/08/15 11:06 AM, Trevor Davel (Twylite) wrote: > On 2012/08/15 09:22 AM, Neil Madden wrote: >> Incidentally, should lmap itself be a concatMap? > No. In the trivial (and most common) case where the map outputs 1 value > per input, each of the output values would need to be list quoted. This > is unnecessary overhead (in source code and runtime work), unintuitive > (*1), and inherently unsafe (*2). > > <snip /> > > This is a semi-contrived example. > > Required: display a value after 1 second has elapsed. > > Attempt #1: after 1000 { puts $value } > Attempt #2: after 1000 puts $value > Attempt #3: after 1000 [subst { puts "$value" }] > Attempt #4: after 1000 [subst { puts {$value} }] > Attempt #5: after 1000 [string map [list @VAL [list $value]] { puts > @VAL }] > Attempt #6: after 1000 [string map [list @VAL [list $value]] { puts > "Prefix: @VAL" }] ;# regression, value can execute > Attempt #7: after 1000 [string map [list @VAL [list $value]] { puts > {Prefix: @VAL} }] ;# value has additional bracing > Attempt #8: after 1000 [string map [list @VAL [list "Prefix: > $value"]] { puts @VAL }] ;# works, but we lose the original value > Attempt #9: after 1000 [string map [list @VAL [list $value]] { set v > @VAL ; puts "Prefix: $v" }] > Attempt #10: # screw it, update to 8.5 > after 1000 [list apply {{value} { puts "Prefix: $value" }} $value] On 15/08/2012 11:53, Trevor Davel (Twylite) wrote: > On 2012/08/15 12:22 PM, Andreas Leitgeb wrote: >>> Attempt #6: after 1000 [string map [list @VAL [list $value]] { puts >>> "Prefix: @VAL" }] ;# regression, value can execute >> I may be naive, but I would have trusted that one. On 15/08/2012 11:53, Trevor Davel (Twylite) wrote: (and the problem with the Attempt #6 approach is ...) > % set secret "1234" ;# luggage combination > 1234 > % set value {[puts "Got it: '$secret'"]} > [puts "Got it: '$secret'"] > % after 1000 [string map [list @VAL [list $value]] { puts "Prefix: > @VAL" }] > after#1 > % update > Got it: '1234' > Prefix: {} Regards, Twylite |