Posted to tcl by sebres at Wed Mar 11 20:29:54 GMT 2020view pretty
# My thoughts trying to explain the concerns around proposed tcl-parser syntax enhancement for better # get-default or a catch fallback with default value (as gracefully return), as opposite to # TIP #323 (Do Nothing Gracefully), or rather to extend or improve it (by "compatible" syntax modification). # Here is my variant: if {[dict get $d $k {:}""] ne ""} { ... } # related dgp's opposite (by the objection "No language level change is needed to make this possible."): if {[? {dict get $d $k} : ""] ne ""} { ... } # As already said the variant with command would be really similar to the "catch", so # indirectly it would "protect" against failures of whole script {dict get $d $k}, # whereas my variant is affecting evaluation of "dict get" only (without to involve the substitution), # so it could still fail if $d or $k are not resolvable as a variables... # from this point of view it is more "precise" applicable. # As for the performance, here is the simple implementation of Don's "proposal" without a language level change: proc ? {command args} { if {[llength $args] > 2 || [lindex $args 0] ne ":"} { return -code "wrong # args: should be \"? command ??:? fallback-value?\"" } if {[info exists ::errorInfo]} { set ei $::errorInfo } if {[info exists ::errorCode]} { set ec $::errorCode } if {![catch {uplevel $command} v]} { return $v } if {[info exists ei]} { set ::errorInfo $ei } else { unset -nocomplain ::errorInfo } if {[info exists ec]} { set ::errorCode $ec } else { unset -nocomplain ::errorCode } return [lindex $args end] } # results (tested with core 8.6th and my fork (see below why), but shows almost the same impact in both): # -- positive case (v exists) -- % proc test {} { set v "existing"; timerate { ? {set v} : default } }; test 2.293243 µs/# 426820 # 436063 #/sec 978.802 net-ms # -- negative case (no variable) -- % proc test {} { timerate { ? {set v} : default } }; test 8.440286 µs/# 117787 # 118479 #/sec 994.156 net-ms # And comparison to enhancement proposal of me (executed in my fork, where I have # a pre-processor that is able to "compile" this to a TEBC with better beginCatch(safe)/endCatch # instructions, that I use to avoid stack traversing and error handling for blocks marked as "safe"): # -- positive case (v exists) -- % proc test {} { set v "existing"; timerate { . v {:}default } }; test 0.011914 µs/# 24569018 # 83932653 #/sec 292.723 net-ms # -- negative case (no variable) -- % proc test {} { timerate { . v {:}default } }; test 0.030033 µs/# 12547012 # 33297185 #/sec 376.819 net-ms # That shows almost 200x performance impact by implementation without "language level change". # and I suppose the fallback (negative) case can be still optimized to the same performance as positive, # if on the fly error generation (here for message "can't read "v": no such variable") would be avoided # by some new interpreter flag or if TEBCResume would get some common additional branch for a fallback result, # so I guess I could reach 0.014 - 0.015 µs/# in negative case (what would make > 600x difference to command "?"). # There are basically the same pros for the syntax enhancement as they were provided at the point # where expansion via {*} (compared to expand commands) gets discussed and implemented.