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.