Artifact [2d1bdca4dd]

Login

Artifact 2d1bdca4dde687603d84007ed4cc5b8e8544ee507e89daedf7ab9ec3ab13b514:


# TIP 174: Math Operators as Commands
	Author:         Kristoffer Lawson <[email protected]>
	Author:         Donal K. Fellows <[email protected]>
	Author:         David S. Cargo <[email protected]>
	Author:         Peter Spjuth <[email protected]>
	Author:         Kevin B. Kenny <[email protected]>
	State:          Final
	Type:           Project
	Vote:           Done
	Created:        15-Mar-2004
	Post-History:   
	Tcl-Version:    8.5
	Tcl-Ticket:     1578137
-----

# Abstract

This TIP describes a proposal for math operators in Tcl as separate commands,
acting much like the equivalent in the Lisp language. This would make simple
usage of mathematics much clearer.

# Rationale

While the **expr** command works fairly well for longer mathematical
expressions, it is extremely tedious for the most common uses, such as
handling indices. Take the following examples:

	 set newList [lrange $list [expr {$idx - 5}] [expr {$idx + 5}]]
	 .c create oval [expr {$x - $r}] [expr {$y - $r}] [expr {$x + $r}] [expr {$y + $r}]

Many find this particular aspect of Tcl unappealing. It gets increasingly
difficult to read as more and more simple mathematical expressions build up.
\(See _Example_ below for how these will look after the proposed change.\)

# Proposed Change

 1. A group of Tcl commands are added which would handle mathematical
    operations without the need to use **expr**. Most commands would take a
    variable amount of arguments and would work such that the operator is
    applied to the combination of the first and second arguments. The result
    of this combination is then used with the operator for the third argument,
    etc. If only one argument is given, it is returned as is. See below for
    details for each operator. An example implementation of the **\+**
    command in Tcl follows:

		 proc ::tcl::mathop::+ {args} {
		     set r 0
		     foreach operand $args {
		         set r [expr {$r + $operand}]
		     }
		     return $r
		 }

 2. All operator commands will be kept in the **::tcl::mathop** \(in line
    with _::tcl::mathfunc_ from [[232]](232.md)\) namespace, from which they would most
    commonly be imported into the calling namespace \(or resolved in it by
    means of the **namespace path** \([[229]](229.md)\) command\).

 3. The commands are not connected to their corresponding **expr** operator.
    Overloading or adding any command in **::tcl::mathop** does _not_
    affect operators in **expr** or any other command that calls
    _Tcl\_ExprObj_, and nor does overriding **expr** alter the behaviour of
    any command in **:::tcl::mathop**.

## Operator Commands Details

Unary operators **~** and **!** always take one argument.

	Op/argc  0    1   2   3+
	~       err  ~a  err  err
	!       err  !a  err  err

Left-associative operators that naturally allow 0 or more arguments do so:

	Op/argc  0   1   2     3+
	+        0   a   a+b   a+b+c...
	*        1   a   a*b   a*b*c...
	&       -1   a   a&b   a&b&c...
	^        0   a   a^b   a^b^c...
	|        0   a   a|b   a|b|c...

Other left or right associative operators. Operator **\*\*** is right
associative, which needs to be noted clearly.

	Op/argc  0   1   2     3+
	**       1   a   a**b  a**(b**(c...))

\(This behaviour depends on the eventual modification of the **\*\*** operator
in [expr] to have right-associativity, which is the subject of [[274]](274.md). If TIP
\#274 fails, **\*\*** should be left- or non-associative.\)

Nonassociative operators \(including the list operators, "**in**" and
"**ni**"\) must always be binary.

	Op/argc   0    1    2      3+
	<<       err  err  a<<b    err
	>>       err  err  a>>b    err
	%        err  err  a%b     err
	!=       err  err  a!=b    err
	ne       err  err  a ne b  err
	in       err  err  a in b  err
	ni       err  err  a ni b  err

Subtract and divide treat their arguments in a left-associative way _except_
for in the unary case. Unary minus is negation, and unary divide is reciprocal.

	Op/argc  0    1      2    3-
	-       err  -a     a-b  ((a-b)-c)...
	/       err  1.0/a  a/b  ((a/b)/c)...

Comparison operators other than **!=** and **ne** test for ordering:

	Op/argc  0  1   2       3+
	<        1  1  a<b     ((a<b)&(b<c)&...)
	<=       1  1  a<=b    ((a<=b)&(b<=c)&...)
	>        1  1  a>b     ((a>b)&(b>c)&...)
	>=       1  1  a>=b    ((a>=b)&(b>=c)&...)
	==       1  1  a==b    ((a==b)&(b==c)&...)
	eq       1  1  a eq b  ((a eq b)&(b eq c)&...)

\(Note the single **&**; a Tcl command is not capable of "short circuit"
evaluation of its arguments.\)

The operators that do conditional evaluation of their arguments \(&&, \|\| and
?:\) are not included. This is because their characteristic evaluation laziness
is best modelled using the existing **if** command.

## Example

As an example use, let us change the lines from above:

	 set newList [lrange $list [- $idx 5] [+ $idx 5]]
	 .c create oval [- $x $r] [- $y $r] [+ $x $r] [+ $y $r]

This is clearly shorter and much easier on the eyes. There is no need to
consider the effects of bracing expressions.

Sum of a list becomes

	 set sum [+ {expand}$list]

# Security considerations

It is worth noting that variadic operators have no way of "short circuit"
evaluation, much as putative **&&** and **\|\|** commands would not. This
consideration means that they must be used with caution in cases where
expressions have side effects; all their arguments will be evaluated.
Commands like

	   < 1 0 [don't do this!]
	   / 0 0 [don't do this!]

will indeed evaluate the string in square brackets.

If expressions like these are constructed from user input, care must be taken
to place them in a safe execution environment or otherwise defend against code
injection attacks. \(This last consideration is somewhat far-fetched, since it
is implausible that an injection attack would be able to generate [< 1 0
[don't do this!]] but not [< [don't do this!] 0].\)

# Implementation

## Efficiency

These commands can naturally be compiled and thus as efficient as their
corresponding **expr** operators. The following lines should probably result
in the same byte codes.

	set x [expr {$a * $b + $c}]
	set x [+ [* $a $b] $c]

## Reference Implementation

Available online at SourceForge
<http://sf.net/tracker/?func=detail&aid=1578137&group_id=10894&atid=310894> .
The patch is for this TIP as it stood several versions ago; in particular, it
does not implement the ordering comparators and gets associativity wrong in a
couple of other cases. Nevertheless, the authors of this TIP believe it to be
an adequate proof that the ideas of the TIP are implementable with good
performance.

# Copyright

This document has been placed in the public domain.