FM A lot of TIPs exists about improving the way we do mathematics with Tcl
TIP 282 aimed to add assignement operator and multilines expressions, to write things like :
expr {
x = 5*$y + 6*$z;
w = $x**2 + $y**2;
v = $w**2 + $y**2
}TIP 420 aimed to allow 2D, 3D and affin transform.
TIP 676 proposed a new command with shorter syntax, removing the needs for a dollar prefix before variables, making barewords valid in expr.
TIP 674 proposed a new command « let », allowing to make many calculus at once.
TIP 672 proposed a shorthand $(expression)
TIP 408 proposed to allow every command to be called from any expression
TIP 309 proposed to expose the expression parser to works with some extended type of numbers
TIP 292 proposed to allow unquoted string in expressions
I produced a TIP .
The source code is stored in a dedicated repository .
The result of the compilation (binary, libs) can be found at https://github.com/florentis/tcl90-exprSH/tree/core-9-0-branch/install_SH
The full TIP option includes :
Example :
# Full options use (133 chars)
for {(i = j = k = l = 0)} {$i < 10} {(i=$i+1; j=$i*2; k=$j*3; l=$k*4)} {
set A([($i+$j+$k+$l)]) [($i*2, $j+2, $k**2, cos($l))]
}
# to be compared with (223 chars):
for {set i [set j [set k [set l 0]]]} {$i < 10} {incr i; set j [expr {$i*2}]; set k [expr {$j*3}]; set l [expr {$k*4}]} {
set A([expr {$i+$j+$k+$l}]) [list [expr {$i*2}] [expr {$j+2}] [expr {$k**2}] [expr {cos($l)}]]
}Example of usage on the wiki :
There were some discussions about this proposal on the tcl-mailing list.
But, despite the links I transmit, the TIP was not published, I don't know why.
Maybe I don't understand the TIP process.
Set vars in a dedicated namespace with a C-like syntax :
namespace eval {(
A = 1;
B = $A+1;
C = $A*$B
)}Simpler switch, if,... returns.
switch $var {
this ("this")
that ($that)
default ([do by default])
}
# Notice that braces can be omitted when there is no space in the script.
if {$test} (1) else (0)Usage with loops
for (i=0) {$i<3} {incr i} {(
# Expression script follow
)}
lmap u $U v $V {($u+$v))set u [(1+1)] ; # basic shorthand (returns a value) [( v=2+2 ; w=3+3 ;)] ; # muted shorthand (returns nothing)
# In scripts
lmap a $A b $B {($a,$b)}
# In Line
set coords [($X0, $Y0, $X1, $Y1)]
[(coords = ($X0, $Y0, $X1, $Y1);)]; # muted versionIt seems there is a strong demand from the community.
TIP 282 faces some problems :
TIP 676, that aim to take barewords as variable, faces this same func / array ambiguity
I propose to distinguish two level of improvement :
I proposed a new syntax [(...)] and created a prototype. It accepts things like :
set hyp [( sqrt($x**2 + $y**2) )]
TIP 282 is a very good proposal. A prototype is working.
The only thing TIP 282 is lacking IMHO is a native list handling. I think we need it.
I proposed to use the « , » comma operator to denote it (it's actually gives a « comma out function argument error »).
Then we can write :
set hyp [( sqrt($x**2 + $y**2) )] .c create rect [( $x, $y, $x+$L, $y+$H )] set Vector [( "X" = 1; "Y" = 2; "X" = 3; $X, $Y, $Z )]
It's better. But the bareword illegalness constraints to enclose the varname in quotes.
So this proposal : Ok, lets take every bareword as a variable, as proposed by TIP 676 .
Let's say we don't need any dollars sign any more to mark a variable
set hyp [( sqrt(x**2 + y**2) )] .c create rect [( x, y, x+L, y+H )] set Vector [( X = 1; Y = 2; X = 3; X, Y, Z )]
It's more clear. But then, it comes the problem of the ambiguity between function and array.
Let me propose to introduce a braced-prefix : {array}, to avoid this potential confusion.
set Rect(width) 300
set Rect(height) 400
return [( sqrt( {array}Rect("width") ** 2, {array}Rect("height") ** 2) )]Of course, this braced-prefix can be generalized to many kinds of collection :
return [(
# Various variables type (TIP 309 needs)
Z = {complex}"2 + 3i";
H = {quaternion}"1 + 3i + 4j + 5k";
L = {list}(1, "potatoes", 3);
O = {dict}("X", 0, "Y", 0);
Id = {Matrix}((1, 0, 0),
(0, 1, 0),
(0, 0, 1));
V1 = {vector}(1, 2, 3)
V2 = {vector}(3, 2, 1)
# projections of collection's components :
n = {complex}Z("i") * {quaternion}H("k") + {complex}Z("");
potatoes = {list}L(1);
Ox = {dict}O("X");
# braced-prefix should be allowed on functions as well
# use any tcl command : (TIP 408 needs)
R0 = {::}lindex(Id,0) ;
C0 = {::}lindex(Id, 0, 0), {::}lindex(Id, 1, 0), {::}lindex(Id, 2, 0);
# Braced-prefix should be allowed to operators as well
# various operations : TIP 420 needs
# cross product
V3 = V1 {cross}* V2;
# scalar product
s = V1 {dot}* V2;
# tensorial product
T = V1 {tensor}* V2;
# Matrix Product
M = {matrix}(V1, V2, V3) {matrix}* Id;
)]Some of those various braced-prefixes would be hard-coded, some other could be made configurable through a special command protocol (TIP 309 need)
protocol expr operator * cross {{
{vector}U
{vector}V
} {
lassign $U x y z
lassign $V u v w
return [( y*w-v*z, z*u-w*x, x*v-u*y)]
}}
protocol expr variable set matrix {args {
return $args
}}
protocol expr variable part matrix {var part {
upvar $var M
return [lindex $M {*}[split $args ","]]
}}
protocol expr variable part array {var part {
upvar $var A
return $A($part)
}}
protocol expr function {::} {func args} {
return [eval [list ::$func {*}$args]]
}}
# ...etcAMB - 2026-02-19 13:32:51
Regarding the bare-word and mathfunc ambiguity, why not just make the rule that for array access, the dollar sign is still required in expressions? Ambiguity removed.
set Rect(width) 300
set Rect(height) 400
return [( Rect(hypot) = sqrt( $Rect("width") ** 2, $Rect("height") ** 2) )]Also, it might be easier to just make the list command available in the mathfunc namespace, so you could do this:
.c create rect {*}[( list(x, y, x+L, y+H) )] Edit: while adding "list" to the mathfunc namespace would be nice, either FM's proposed {::} prefix or my proposed {command}(arg,arg...) syntax would make this unnecessary. See below.
<<FM>> But then, what do you propose to be able to work with complex, quaternion, octonion, vector, matrix, tensor ... ? To assign them a value, to apply them a set of operation ? What do you suggest to be able to call any Tcl command from expr ?
Well, your idea works. But, if your idea is to make one rule per thing, you will have to make a lot of rules ! No, the better is to find the minimal set of rules which allows to make a maximum of things. <</FM>>
<<AMB>>
One thing at a time, FM, one thing at a time. I was commenting on the bare-words in expressions. But here are my thoughts on the other parts of this page:
For dealing with vectors/matrices/tensors, I think that having dot-operators (e.g. ".+" or ".*" to designate element-wise addition/multiplication) could work. Alternatively, there can be special math functions for these element-wise operations. See this project for some good examples.
For calling any command from expr, why not use braced prefixes? It returns an error in math expressions (it says "missing operator").
Then, your proposed use of prefixes for things like {dict} and {string} work just as intended, and you can use any other Tcl command, including ones from packages.
# Accessing any Tcl command in a mathfunc with {command}(arg,arg,...) syntax
set mylist {1 2 3 4 5}
set i 0
puts [( {lindex}(mylist,i+1) * 5 )]; # 10
set x 5
puts [( mydict = {dict}("key1",6,"key2",double(x)+4) )]; # key1 6 key2 9.0
# Example with Tcllib functions
package require math::linearalgebra
puts [(
{math::linearalgebra::crossproduct}({list}(3,-3,1),{list}(4,9,2))
)]; # -15 -2 39This would not conflict with the {*} expansion prefix, which is only parsed within command evaluation. <</AMB>>
AMB Edit: So I would like [(...)] to return a list, with elements separated by commas. But this would no longer be shorthand for expr, which returns a single value. If expr were changed to return multiple values, it would break existing code. For example:
puts [expr {"hello world"}]; # hello world
puts [("hello world")]; # {hello world}So, what if, as a stepping stone to get here, we introduce the command "lexpr", which, as I am defining it here, would return a list of values, with the values separated by commas. It would be almost identical to my exprstar prototype.
puts [lexpr {"hello world"}]; # {hello world}
set x 5
set y 3
puts [lexpr {$x+1,$y+2,$x+$y}]; # 6 5 8Then, the [(...)] syntax would be shorthand for lexpr, not expr. Side note, FM, your [(...)] math notation is growing on me.
AMB: On my drive to work, I was thinking more about the {command}(args) notation for accessing Tcl commands in a math expression. To clarify, it should be more like {commandPrefix}(args). I would like to clarify what I am thinking about with an example:
oo::class create foo {
variable i
constructor {} {
set i 2
}
method bar {incr} {
incr i $incr
}
}
set myfoo [foo new]
set x 5
# puts [expr {2 * [$myfoo bar [expr {$x+1}]]}]; # old way
puts [(2 * {$myfoo bar}(x+1))]; # 16This would greatly enhance Tcl, IMO.
Anyways, back to work.
AMB: Another thought, what if the syntax (x,y,z) represented a list? As in, if there is no bare-word, or {commandPrefix} before a set of parentheses, it interprets it as basically {list}(x,y,z). Then you wouldn't need my proposed "lexpr" modification, if you wanted multiple values returned from an expression, you'd just wrap it in parentheses with commas separating the values.
Then, the oft-cited reason for these improvements (entering coordinates for Tk widgets) would look like this
.c create rect {*}[(( x, y, x+L, y+H ))] <<FM>> This is exactly what I was thinking about. Event more : if there is no ambiguity, there would be no need to enclose it between parenthesis.
# no ambiguity
.c create rect {*}[( x, y, x+L, y+H )]
# ambiguities
set V1 [( (1,2,3) + (2,4,5) )]
set V2 [( 3 * (1,2,3) )]<</FM>>
<<AMB>> I think there is a misunderstanding. The expr command returns a scalar, not a list. If you suddenly had math expressions returning a list, with elements separated by commas, it would break existing code that has expr return a list or a string with spaces. And if for some reason the [(...)] syntax returned a list rather than a scalar, it wouldn't be true shorthand for [expr {...}]. It would have to be shorthand for a different math function, such as the "lexpr" concept I proposed.
So, I am proposing that (x,y,z), without any prefix, is native syntax for a list within an expression. No need to add "list" to the mathfunc namespace, and no need for an additional math command.
Here are some examples:
# Example of a procedure that returns two values
proc area_and_perimeter {length width} {return [(
(length*width, 2*(length+width))
)]}
puts [area_and_perimeter 3 4]; # 12 14
# If you want to expand multiple math arguments, you would have to add an additional set of parentheses to denote that it is a list.
.c create rect {*}[( (x, y, x+L, y+H) )]
# For element-wise operations, we could either define special dot-operators (e.g. .+ , .- , .*) that do operations on lists, you could define special mathfunctions for adding/subtracting vectors, or you could import existing commands for vector and matrix operations and use the {commandPrefix}(args) notation I mentioned.
# Dot-operator option (would require adding operators to the expr engine)
set V1 [( (1,2,3) .+ (2,4,5) )]
set V2 [( 3 .* (1,2,3) )]
# Special math functions (see https://github.com/ksaalfeld/exprplusplus)
package require expr++
set V1 [( add((1,2,3),(2,4,5)) )]
set V2 [( mul(3,(1,2,3)) )]
# Tcl commands for manipulating vectors (see https://core.tcl-lang.org/tcllib/doc/trunk/embedded/md/tcllib/files/modules/math/linalg.md)
package require math::linearalgebra
namespace import ::math::linearalgebra::scale ::math::linearalgebra::add
set V1 [( {add}((1,2,3),(2,4,5)) )]
set V2 [( {scale}(3,(1,2,3)) )]<</AMB>>