From: Brian G. <bri...@me...> - 2012-08-19 07:07:01
|
TIP #408: ALLOW ANY COMMAND FOR EXPR FUNCTIONS ================================================ Version: $Revision: 1.1 $ Author: Brian Griffin <brian_griffin_at_mentor.com> State: Draft Type: Project Tcl-Version: 8.7 Vote: Pending Created: Friday, 17 August 2012 URL: http://purl.org/tcl/tip/408.html WebEdit: http://purl.org/tcl/tip/edit/408 Post-History: ------------------------------------------------------------------------- ABSTRACT ========== Proposed expansion of what constitutes a function in an *expr* expression. RATIONALE: NESTED EXPR CALLS ============================== The *expr* command in Tcl is the only way to perform math and comparison operations. Tcl's variable and command substitution rules allow for great flexibility, although the syntax does not lead to great readability, especially due to the ordering imposed on substitutions. For example, in the expression: expr {[lindex $data $start] > 4} the command substitution ([...]) is performed before the expression can be parsed for evaluation, and, in order for the command substitution to be parsed, the variable substitution ($) must be performed. The consequences of this ordering means that arguments to commands cannot themselves be expressions unless the argument is itself a command substitution involving *expr*. This means, for example, that to compute an index value, *expr* must be recursively called: expr {[lindex $data [expr {$start + 2}]] > 4} However, when using a math function, the command substitution ordering does not apply since the function is part of the expression syntax: expr {pow($x + 2, 2)} It seems reasonable and useful if any Tcl command can be called using function syntax: expr {lindex($data, $start + 2) > 4} The common command substitution pattern has lead many developers to end up writing things like: if {[expr {$a+$b}] > 7} ... In hindsight, such statements look ridiculous, but it works and it follows the same pattern as the *lindex* case above. If subexpressions could be evaluated directly by the enclosing *expr* operations, this would greatly simplify the overall expression and make it more readable and thereby more maintainable, and hopefully, less error prone. The goal here is to reduce the instances of recursive *expr* calls in an expression. It seems overly complex and confusing to read, and to have to use command substitution syntax inside what is already an *expr* operation. PROPOSED CHANGES ================== The proposal is to simply apply normal Tcl command resolution rules to *expr* functions. The one caveat is that *tcl::mathfunc* namespace is always searched first. I see this as a continuation to [TIP #232]. REJECTED ALTERNATIVES ======================= This can already be accomplished today by creating alias functions in the *tcl::mathfunc* namespace, but this mechanism is awkward in that: 1. It must be defined beforehand for the set of commands used or intended to be used in expressions, 2. For OO and namespace code, some specialized duplication is also required, and 3. It can lead to confusion when it's not clear why some commands work as functions and others don't. PROOF OF CONCEPT ================== (Thanks and credit to Frédéric Bonnet) A quick & dirty proof of concept with unknown handlers: proc mathproc {cmd args} { if {[namespace qualifiers $cmd] eq "tcl::mathfunc"} { return [uplevel [list [namespace tail $cmd]] $args] } uplevel [list ::unknown $cmd] $args } namespace unknown mathproc set l [expr {list(1,2,3)}] => 1 2 3 expr {lindex($l,2)} => 3 expr {expr(join($l,"+"))} => 6 REFERENCE IMPLEMENTATION ========================== None currently COPYRIGHT =========== Copyright (c) 2012 by GriffinTk This document is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License [<URL:http://creativecommons.org/licenses/by-sa/3.0/deed.en_US>]. ------------------------------------------------------------------------- TIP AutoGenerator - written by Donal K. Fellows |
From: Donald G P. <don...@ni...> - 2012-08-20 16:18:14
|
On 08/19/2012 03:06 AM, Brian Griffin wrote: > For example, in the expression: > > expr {[lindex $data $start]> 4} > > the command substitution ([...]) is performed before the expression can > be parsed for evaluation, and, in order for the command substitution to > be parsed, the variable substitution ($) must be performed. That is completely wrong. -- | Don Porter Applied and Computational Mathematics Division | | don...@ni... Information Technology Laboratory | | http://math.nist.gov/~DPorter/ NIST | |______________________________________________________________________| |
From: Brian G. <bri...@me...> - 2012-08-20 17:58:13
|
On Aug 20, 2012, at 9:18 AM, Donald G Porter wrote: > On 08/19/2012 03:06 AM, Brian Griffin wrote: > >> For example, in the expression: >> >> expr {[lindex $data $start]> 4} >> >> the command substitution ([...]) is performed before the expression can >> be parsed for evaluation, and, in order for the command substitution to >> be parsed, the variable substitution ($) must be performed. > > That is completely wrong. What I was trying to say was that it is not possible to write: expr {[lindex $data ($a+$b)] > 4} # bad index "(7+3)": must be integer?[+-]integer? or end?[+-]integer? Sorry for the poorly written and incorrect description of why it's not possible. -Brian |
From: Donald G P. <don...@ni...> - 2012-08-20 18:11:46
|
On 08/20/2012 01:58 PM, Brian Griffin wrote: > What I was trying to say was that it is not possible to write: > > expr {[lindex $data ($a+$b)]> 4} > # bad index "(7+3)": must be integer?[+-]integer? or end?[+-]integer? Well, current Tcl provides several answers to that particular example. You could start by just dropping the parens, though that has limited application. % set data {1 2 3 4 5 6 7 8 9 10 11 12} 1 2 3 4 5 6 7 8 9 10 11 12 % set a 7 7 % set b 3 3 % expr {[lindex $data $a+$b] > 4} 1 Or you could make use of the operator commands. % namespace path tcl::mathop % expr {[lindex $data [+ $a $b]] > 4} 1 The math functions are also available as commands, so I really can't think of any case where the use of a nested [expr] is still required. Unless you're in [if] or [while] or something forcing the issue, the outer [expr] isn't required anymore either. % > [lindex $data [+ $a $b]] 4 1 So if you're in a coding situation where nested [expr]s are the vexing problem, I'd first want to know why you're not using the existing solutions. -- | Don Porter Applied and Computational Mathematics Division | | don...@ni... Information Technology Laboratory | | http://math.nist.gov/~DPorter/ NIST | |______________________________________________________________________| |
From: Brian G. <bri...@me...> - 2012-08-20 19:50:55
|
On Aug 20, 2012, at 11:11 AM, Donald G Porter wrote: > On 08/20/2012 01:58 PM, Brian Griffin wrote: >> What I was trying to say was that it is not possible to write: >> >> expr {[lindex $data ($a+$b)]> 4} >> # bad index "(7+3)": must be integer?[+-]integer? or end?[+-]integer? > > Well, current Tcl provides several answers to that particular example. > You could start by just dropping the parens, though that has limited > application. > > % set data {1 2 3 4 5 6 7 8 9 10 11 12} > 1 2 3 4 5 6 7 8 9 10 11 12 > % set a 7 > 7 > % set b 3 > 3 > % expr {[lindex $data $a+$b] > 4} > 1 > > Or you could make use of the operator commands. > > % namespace path tcl::mathop > % expr {[lindex $data [+ $a $b]] > 4} > 1 > > The math functions are also available as commands, so I really can't > think of any case where the use of a nested [expr] is still required. > Unless you're in [if] or [while] or something forcing the issue, the > outer [expr] isn't required anymore either. > > % > [lindex $data [+ $a $b]] 4 > 1 > > So if you're in a coding situation where nested [expr]s are the vexing > problem, I'd first want to know why you're not using the existing > solutions. 1) lots of legacy code. 2) lots of coding by pattern. (See 1 above) -Brian |
From: Brian G. <bri...@me...> - 2012-08-20 20:18:35
|
On Aug 20, 2012, at 11:11 AM, Donald G Porter wrote: > On 08/20/2012 01:58 PM, Brian Griffin wrote: >> What I was trying to say was that it is not possible to write: >> >> expr {[lindex $data ($a+$b)]> 4} >> # bad index "(7+3)": must be integer?[+-]integer? or end?[+-]integer? > > Well, current Tcl provides several answers to that particular example. > You could start by just dropping the parens, though that has limited > application. > > % set data {1 2 3 4 5 6 7 8 9 10 11 12} > 1 2 3 4 5 6 7 8 9 10 11 12 > % set a 7 > 7 > % set b 3 > 3 > % expr {[lindex $data $a+$b] > 4} > 1 > > Or you could make use of the operator commands. > > % namespace path tcl::mathop > % expr {[lindex $data [+ $a $b]] > 4} > 1 > > The math functions are also available as commands, so I really can't > think of any case where the use of a nested [expr] is still required. > Unless you're in [if] or [while] or something forcing the issue, the > outer [expr] isn't required anymore either. Actually, [if] and [while] are the majority cases where nested [expr] appears, even the occasional statements like this: if {[expr {$fullExpand & 2}]} { ... while {$strl < $strend && [expr {$wstr + $bwSum}] > $itk_option(-minwidth)} { ... This is real, un-doctored code. -Brian |
From: Kevin K. <kk...@ny...> - 2012-08-20 21:09:46
|
On 08/20/2012 04:18 PM, Brian Griffin wrote: > Actually, [if] and [while] are the majority cases where nested [expr] appears, > even the occasional statements like this: > > if {[expr {$fullExpand & 2}]} { ... > > while {$strl < $strend && [expr {$wstr + $bwSum}] > $itk_option(-minwidth)} { ... > > This is real, un-doctored code. Wow, this code could *use* some doctoring. And code like this will be helped by searching for math functions in the current namespace .... uhmmm, how, exactly? -- 73 de ke9tv/2, Kevin |
From: Brian G. <bri...@me...> - 2012-08-20 23:57:57
|
On Aug 20, 2012, at 2:09 PM, Kevin Kenny wrote: > On 08/20/2012 04:18 PM, Brian Griffin wrote: > >> Actually, [if] and [while] are the majority cases where nested [expr] appears, >> even the occasional statements like this: >> >> if {[expr {$fullExpand & 2}]} { ... >> >> while {$strl < $strend && [expr {$wstr + $bwSum}] > $itk_option(-minwidth)} { ... >> >> This is real, un-doctored code. > > Wow, this code could *use* some doctoring. > > And code like this will be helped by searching for math functions in the > current namespace .... uhmmm, how, exactly? My theory is that the above happens because it follows a very common pattern. The pattern: set a [lindex $data [expr {$a+$b}]] is very common. The pattern: if {[lindex $data [expr {$a+$b}]] < 7} {... is less common, but still common. Type or read enough of those, your are very likely to type: if {[expr {$a+$b}] < 7} {... without thinking about, and it works, which reenforces the behavior. My idea is that if the [expr] language was pure (purer), then there just wouldn't be that common pattern in the code you're reading and, over time, less likely to repeat it. This idea falls apart if the expression contains user functions and you are force to: if {[getXY $w [expr {$width / $part}]] > 0} {... Unless the user knows to add the alias to the mathfunc namespace, you're right back where you started. On the other hand, if it "just worked", then all the better: if {getXY($w, $width / $part) > 0} {... That's my theory anyway. It came about while perusing ~500,000 lines of Tcl code. Feel free to shoot it down. :) -Brian |
From: Donal K. F. <don...@ma...> - 2012-08-28 10:19:33
Attachments:
donal_k_fellows.vcf
|
On 20/08/2012 21:18, Brian Griffin wrote: > Actually, [if] and [while] are the majority cases where nested > [expr] appears, even the occasional statements like this: > > if {[expr {$fullExpand & 2}]} { ... > > while {$strl < $strend && [expr {$wstr + $bwSum}] > $itk_option(-minwidth)} { ... > > This is real, un-doctored code. FWIW, these compile to pretty efficient code. The current compiler (heck, the one in 8.5 too) does a good job with nested constant expressions. Doesn't stop them from being ugly. :-) Donal. |
From: Brian G. <bri...@ea...> - 2012-08-28 14:41:28
|
On Aug 28, 2012, at 3:19 AM, Donal K. Fellows wrote: > On 20/08/2012 21:18, Brian Griffin wrote: >> Actually, [if] and [while] are the majority cases where nested >> [expr] appears, even the occasional statements like this: >> >> if {[expr {$fullExpand & 2}]} { ... >> >> while {$strl < $strend && [expr {$wstr + $bwSum}] > $itk_option(-minwidth)} { ... >> >> This is real, un-doctored code. > > FWIW, these compile to pretty efficient code. The current compiler > (heck, the one in 8.5 too) does a good job with nested constant expressions. That's good to know. > > Doesn't stop them from being ugly. :-) I also stopped worrying about small performance bits when writing C code too in favor of readability since the optimizer should be taking care of this. -Brian |
From: Andreas K. <and...@ac...> - 2012-08-28 18:31:57
|
On 8/28/2012 3:19 AM, Donal K. Fellows wrote: > On 20/08/2012 21:18, Brian Griffin wrote: >> Actually, [if] and [while] are the majority cases where nested >> [expr] appears, even the occasional statements like this: >> >> if {[expr {$fullExpand & 2}]} { ... >> >> while {$strl < $strend && [expr {$wstr + $bwSum}] > >> $itk_option(-minwidth)} { ... >> >> This is real, un-doctored code. TDK tclchecker will warn about stuff like this. Requires -Wall, -W4, or -check warnNestedExpr tough. -- Andreas Kupries Senior Tcl Developer Code to Cloud: Smarter, Safer, Faster™ P: 778.786.1122 F: 778.786.1133 and...@ac... http://www.activestate.com Learn about Stackato for Private PaaS: http://www.activestate.com/stackato http://www.tcl.tk/community/tcl2012/ - Tcl'2012, Nov 12-16, Chicago, IL, USA. |
From: Donald G P. <don...@ni...> - 2012-08-20 16:27:17
|
On 08/19/2012 03:06 AM, Brian Griffin wrote: > > TIP #408: ALLOW ANY COMMAND FOR EXPR FUNCTIONS > ================================================ % namespace export * % namespace eval tcl::mathfunc namespace import ::* % expr {lindex({one two three}, 1)} two -- | Don Porter Applied and Computational Mathematics Division | | don...@ni... Information Technology Laboratory | | http://math.nist.gov/~DPorter/ NIST | |______________________________________________________________________| |
From: Donald G P. <don...@ni...> - 2012-08-20 16:44:11
|
On 08/19/2012 03:06 AM, Brian Griffin wrote: > TIP #408: ALLOW ANY COMMAND FOR EXPR FUNCTIONS > ================================================ Be wary if this concept goes further that there are glitches in the passing of arguments to, and retrieving results from math function commands regarding whether any value has to be forced to numeric interpretation, or can survive as "just a string". (Tcl Bugs 3408408 and 3107983 and as mentioned in comments there, I know there's a conversation somewhere on this point, but I can no longer find it. Possibly in TCLCORE?) Also be aware that the [expr] parser imposes limits on function names, so not all commands can be accommodated. (Tcl Feature Request 1511357). Passing switches to commands that take them through the math function syntax is at least awkward. % expr {lsort("-integer", list(3,2,1))} 1 2 3 Perhaps that's not too bad, but you can imagine more complex cases, and then imagine explaining in a tutorial where quoting is and is not required. At best, I think only some commands are really well-suited to act as functions in expressions. As such, I think an approach which has the programmer choose the right set, rather than including them all auto-magically, is the better approach. Since that's already provided for, I'd not embrace this TIP. -- | Don Porter Applied and Computational Mathematics Division | | don...@ni... Information Technology Laboratory | | http://math.nist.gov/~DPorter/ NIST | |______________________________________________________________________| |