[AMG]: This page discusses TIP 174 [http://tip.tcl.tk/174], "Math Operators as Commands". TIP 174 adds commands named '''+''', '''-''', '''==''', etc., for the purpose of exposing math operations to the script level without needing to go through [[[expr]]]. ---- The following table lists all TIP 174 commands and defines their behavior by showing their [expr]ession equivalents. Parentheses are used to indicate associativity. Operation or test Cmd 0 1 2 3 or more arguments Bitwise negation ~ err ~a err err Logical negation ! err !a err err Arithmetic negation - err -a Sum + 0 a a+b ((a+b)+c)+... Product * 1 a a*b ((a*b)*c)*... Shift left << err err a<> err err a>>b err Bitwise and & -1 a a&b ((a&b)&c)&... Bitwise inclusive or | 0 a a|b ((a|b)|c)|... Bitwise exclusive or ^ 0 a a^b ((a^b)^c)^... Numeric equality == 1 1 a==b ((a==b)&(b==c))&... String equality eq 1 1 a eq b ((a eq b)&(b eq c))&... Numeric inequality != err err a!=b err String inequality ne err err a ne b err List membership in err err a in b err List non-membership ni err err a ni b err Strict increasing order < 1 1 a 1 1 a>b ((a>b)&(b>c))&... Decreasing order >= 1 1 a>=b ((a>=b)&(b>=c))&... The '''-''' command is something of a changeling. In its unary form, it negates its first (and only) argument. For higher arity, it negates every argument ''except'' its first and returns the sum. While this may seem counterintuitive, it does match the behavior of the '''-''' [expr] operator. Exponentiation ('''**''') is right-associative. Short-circuit logic ('''&&''', '''||''') isn't supported because it cannot be implemented in Tcl. Tcl always eagerly determines the value of each argument ''before'' passing it to the command. For this, just keep using [expr]: % expr {1==1 && [puts hello] eq ""} hello 1 % expr {1==0 && [puts hello] eq ""} 0 % expr {1==0 || [puts hello] eq ""} hello 1 % expr {1==1 || [puts hello] eq ""} 1 Shift right is arithmetic, not bitwise, meaning that the sign bit is unchanged by the operation. For two's-complement machines, arithmetic and bitwise shift left produce the same results. ---- [AMG]: When I first read the TIP, I wondered why the inequality operators were strictly binary. It seemed to me that they could be more general, that they could test to see if all elements of a list are distinct. In the zero- and one-argument cases, they would return true. In the two-argument case, they'd behave as expected. For three or more arguments, each possible argument pairing would be checked for inequality, e.g. '''(a!=b)&(a!=c)&(b!=c)''' for three arguments. Today I asked the [Tcl Chatroom], and the answer was that inequality operators are binary-only because the interpretation for other arities is ''not'' obvious; multiple reasonable interpretations exist. 1. Pairwise inequality: '''(a!=b)&(b!=c)&(c!=d)''' 2. Any inequality: '''(a!=b)|(a!=c)|(a!=d)|(b!=c)|(b!=d)|(c!=d)''' 3. Total inequality: '''(a!=b)&(a!=c)&(a!=d)&(b!=c)&(b!=d)&(c!=d)''' In the above table, every operator that ''does'' support three or more arguments (I'll call them ternary+ operators) has a commonly understood meaning when written '''a op b op c op d''', for instance '''a == b == c == d'''. Never is '''a != b != c != d''' written because it would not be clear which of the above three interpretations is intended. Moreover, for every ternary+ operator, interpretations (1) and (3) always yield the same result, assuming the list ordering isn't reversed when using asymmetric operators. This is because they all exhibit transitivity: '''(((a op b) and (b op c)) implies (a op c))'''. Try it and see. :^) [RS] pointed out that (2) is simply '''[[! [[== a b c d]]]]'''. He also suggested a nice, simple implementation of (3) using [[[lsort]]]: proc distinct {args} {== [llength $args] [llength [lsort -unique $args]]} (Use the '''-real''' [lsort] option if you don't want "0", " 0", "0.0", "-0", etc. to be considered distinct.) I can't imagine a single case where (1) is useful, but it wouldn't be difficult to implement. [wdb] In my humble opinion, in opposite to the operator ==, the operator != ''cannot'' be transitive. Watch this infix notation: 0 != 1 != 0 You can translate it to ''0 != 1 && 1 != 0'', but would you ever say because part 1 is true and part 2 is true, that the conclusion were ''0 != 0''? [AMG]: Yes, the whole problem is that != isn't transitive, which is why the ambiguity exists. '''[[!= 0 1 0]]''' is true according to interpretations (1) and (2) but false according to interpretation (3). For any transitive operator, (1) and (3) always yield the same result. ---- [wdb] At moment, the lazy evaluating operator ''&&'' cannot be implemented. Just an idea ... in the form above, it is not possible to realise ''&&'' with lazy evaluation as every arg is evaluated when the function is invoked. But if we braced the list of args? Watch: && {expr1 expr2 ...} results in the same bytecode as expr {expr1 && expr2 ...} See also the ternary operator ''?'' - [RS] ... which is of course [if]...''else''... [AMG]: Would this behavior (automatic application of [expr]) be applied to all math operator commands or only to '''||''' and '''&&'''? The problem I see is that it would effectively make quoting required for all arguments, same as it is with [[[expr]]] itself. See [brace your expr-essions] for the reasons why. [wdb] Both of you ([RS] and [AMG]) are right. One of my [quick'n'dirty] ideas. What if the operator (e.g.) ''+'' as well as ''&&'' would concat and evaluate their arguments as well as nowadays [expr]? Example: && $expr1 $expr2 should be equivalent to && {$expr1 $expr2} In the latter case of example above, $expr2 is not evaluated if $expr1 results in [false]; in the former case, both are evaluated. The result should be the same unless $expr1 is false and $expr2 produces some side effect such as an error (but not only). I am not sure if I have overseen some pitfalls. But is this idea good? Or bad? [AMG]: Bad, I think, but I only say so because it gets in the way of the use I envision for operator commands. I find bracing my [expr]essions to be a chore, and I look forward to not having to do this when using math operator commands. Combine this with the ::[tcl]::[mathfunc] [namespace], and I won't need [[[expr]]] at all. I'd have to use [[[if]]] and friends to make up for the lack of conditionals and short-circuit evaluation, but this is fine because I rarely use these outside of the first argument to [[[if]]] and [[[while]]]. By the way, this change makes me much less interested in '''$[[...]]''' shorthand for '''[[[expr] {...}]]'''. I simply won't need it. [wdb] But note that [expr] is part of the core as it is invoked implicitly as arg#1 for [if], [while], and arg#3 for [for]. -- Generally there comes some new dichotomy as ''expr {$a + $b}'' and ''+ $a $b'' do the same job. ---- [LES] I hope that TIP 174 never makes it to the core. That is all perfectly possible with sugar syntax. My position is: don't change the language except to implement something that is really not possible with the existing syntax and commands. If you want everything prêt-à-porter, make your own procs and classes and '''reuse''' them in every opportunity, or just code in [PHP] and its 1,000+ TV dinner functions. [AMG]: Perhaps something similar to TIP 174 can be placed in [tcllib] to make it commonly available. [Lars H]: You hope in vain, '''LES'''; TIP#174 is already (2007-01-13) part of the 8.5 core. An important reason for having it is that the commands already exist a bytecode operations, so its implementation was only about adding direct Tcl commands that compile to these things without taking the detour through the [expr] [little language]. ---- [PWE] Wouldn't it be nice if the commands were also able to handle lists, and process them as array's or vectors? [RS]: See [vector] for links to a number of such extensions/code snippets. If they treat a scalar like the original operator, it might indeed be possible to overload math operators in that way... [Lars H]: A good thing about having the math operations as commands in the tcl::mathop namespace is that you can easily have corresponding operations on vectors (defined the way ''you'' want them) in another namespace. In Tcl, one picks a command made for the type of data one wishes to operate on, rather than making commands that try to guess which operation should be applied based on the data they're fed. ---- [AJD] A couple of nice-to-have builtin operator commands would be: [[<=>]] (numeric comparison) and [[cmp]] (string comparison) as Perl has. These crop up when writing sort comparison commands. Of course you can write proc <=> {a b} { if {$a > $b} { return 1 } ; if {$b > $a} { return -1 } ; return 0 } but that's no different to any of the other op's recently introduced. [RS]: If I understand right, ''cmp'' is what Tcl calls [string compare]; a <=> could be done as sgn($a-$b), where the classic definition by [rmax] is proc sgn x {expr {($x>0)-($x<0)}} [DKF]: Surely you mean: proc tcl::mathfunc::sgn x {tcl::mathop::- [tcl::mathop::> $x 0] [tcl::mathop::< $x 0]} [RS] Do I? Maybe as a compromise, with [namespace import]ed operators: proc tcl::mathfunc::sgn x {- [> $x 0] [< $x 0]} But still I think, an [expr] with two or more different operators in play looks better (clearer) than a nest of prefix operators... [LV] AJD, how do your <=> and cmp differ from Tcl [expr]'s == and eq? [AJD] ...see the above proc definition for [[<=>]]. While, as has already been noted, [[cmp]] is really just [[string compare]]. It should be obvious how they differ to == and eq: for example [[== 0 1]] is 0 while [[<=> 0 1]] is -1. The typical usage would be in a sort compasion routine: proc my_cmp {a b} { set int_a [do_something_that_returns_an_integer $a] set int_b [do_something_that_returns_an_integer $b] return [<=> $int_a $int_b] ;# or perhaps more readably as: [expr {$int_a <=> $int_b}] } set sorted [lsort -command my_cmp $some_list] [LV] I see - you are right, those are useful functions. So, the next question is this - can one use the [ensemble] functionality in Tcl 8.5 to write cmp and <=>, in a way that, as a result, expr would have them? ---- KD: It is unfortunate that the ternary ? operator was not included in TIP 174. When I look at my programs, almost half of the cases where I could use "operators as commands" involve the ternary operator. Usually for cases like: $w configure -state [expr {$s ? "normal" : "disabled"}] which becomes much cleaner when written as: $w configure -state [? $s normal disabled] The TIP states that the ternary operator was not included because it has lazy evalution semantics in expr (when properly braced), but that is not a problem IMHO. [RS] On the other hand, as [if] is a function that returns the result of the succeeding script, how about proc I x {set x} ;# identity $w configure -state [if $s {I normal} {I disabled}] [AMG]: Going a step farther, you're free to make your own "ternary operator" command. Note that I brace the first argument to [[if]] because it is an [expr]. proc ? {test true false} {if {$test} {return $true} {return $false}} I often find myself using '''?:''' with more than just three arguments, for example (C): int i = input(); int o = i == 0 ? 99 : i == 1 ? 98 : i == 2 ? 97 : i == 3 ? 96 : i == 4 ? 95 : 94; Writing this using [[?]] might yield the following (Tcl): set i [input] set o [? [== $i 0] 99 [? [== $i 1] 98\ [? [== $i 2] 97 [? [== $i 3] 96\ [? [== $i 4] 95 94]]]]] To avoid excess brackets, a more general '''?''' operator can be made that would work like [[if ... elseif ... elseif ... else]]: proc ? {args} { if {[llength $args] % 2 != 1} { error "wrong # args: should be \"? ?test1 out1 test2 out2 ...? default\"" } while {[llength $args] > 2} { set args [lassign $args test true] if {$test} {return $true} } return [lindex $args end] } I don't use the math operator commands in the above code because all math is done inside the first argument to [[[if]]] or [[[while]]] which already understands the [expr] language. Rewritten example: set i [input] set o [? [== $i 0] 99 [== $i 1] 98\ [== $i 2] 97 [== $i 3] 96\ [== $i 4] 95 94] Unlike with C, all the comparisons are performed ''before'' invoking the '''?''' operator. If the tests have side effects, this is a problem. But I prefer to not have side effects in my tests, so it's okay with me, and the only remaining problem is the slight performance decrease. It is possible to give [[?]] the power of short-circuit evaluation simply by removing the braces from its [[[if]]] test, and then all test arguments to [[?]] will need to be braced to avoid immediate evaluation. But for this facility to support variables and such, an [[[uplevel]]] is needed too. To make the short-circuit evaluation behavior complete, do the same for the values returned by [[?]], and brace every argument to [[?]]. proc ? {args} { if {[llength $args] % 2 != 1} { error "wrong # args: should be \"? ?test1 out1 test2 out2 ...? default\"" } while {[llength $args] > 2} { set args [lassign $args test result] if [list [uplevel [list expr $test]]] "return \[uplevel [list expr $result]\]" } set result [lindex $args end] return [uplevel [list expr $result]] } Safely constructing scripts to pass to [[[uplevel]]] can be a pain, but I think I successfully avoided [quoting hell]. (Tcl offers spiritual liberation! Put ''that'' in the man page!) Usage example: % set x 5 % ? {$x == 5} {$x * 2} {$x == 6} {$x * 3} {$x - 1} 10 % set x 6 % ? {$x == 5} {$x * 2} {$x == 6} {$x * 3} {$x - 1} 18 % set x 7 % ? {$x == 5} {$x * 2} {$x == 6} {$x * 3} {$x - 1} 6 ---- [[ [Category Language] | [Category Mathematics] ]]