Posted to tcl by sebres at Wed Mar 11 20:29:54 GMT 2020view raw

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