expr*

Difference between version 5 and 6 - Previous - Next
** Synopsis **

'''expr*''' ''arg'' ?''arg arg ...''?

** Description **

`expr*` evaluates each of its arguments with [expr] and returns the list of results.

`expr*` with a single argument yields the same result as [expr] with a single argument.


** Implementation **

======
proc expr* args {lmap e $args {uplevel 1 [list expr $e]}}
======
With tailcall:
======
proc expr* args {tailcall lmap ___e $args {expr $___e}}
======
Implementations by [dkf]. Idea offered by [LEG] as one of the responses to the perennial expr discussion in 2023.

** Discussion **

`expr*` differs from [expr] by its interface. [expr] concats it's arguments and evaluates the result in it's own little language. This can lead to unwanted results via [double substitution]. For standard usage and beginners it is recommended to always [brace your expr-essions], resulting in general usage of a single argument for [expr].

`expr*` requires each expression to be braced or quoted anyway.

Several quests and TIPs evolve around the perceived unhandyness of expr in certain situations. I contend
that some of them can be helped by expr*'s different interface.

Example, taken from the chat: canvas operations involve specification of coordinate lists:
======
.canvas create rectangle [expr {$w-100}] [expr {$h-40}] [expr {$w+100}] [expr {$h+40}]
======
With expr*:
======
.canvas create rectangle {*}[expr* {$w-100} {$h-40} {$w+100} {$h+40}]
======
Note: the same with [mathop]:
======
.canvas create rectangle [- $w 100] [- $h 40] [+ $w 100] [+ $h 40] 
======
----
'''[AMB] - 2025-07-02 20:43:49'''

In [Expanding the Expansion Prefix], I suggest that the prefix {=} is added, which processes multiple expressions that are comma-separated, like how arguments are processed in math functions, and automatically expands the arguments like the {*} prefix.

Then, the canvas example provided here could be cleaned up even further:

======
.canvas create rectangle {=}{$w-100, $h-40, $w+100, $h+40}
======

Edit: Alternatively, the prefix {c} could be used to emphasize that it is comma-separated expressions. You could also use it for all arguments in a command if you prefer that syntax, but I see the main use-case to be when you have a lot of math expressions in your command inputs.
======
.canvas {c}{"create", "rectangle", $w-100, $h-40, $w+100, $h+40}
======

Edit: While I would love to see the {c} prefix added to Tcl, it is something that would require a substantial effort on the source-code level. Further discussion in [Expanding the Expansion Prefix] led to discussion of introducing a simple math function called [::tcl::mathfunc::list], that would simply return the arguments.

This would allow you do to the following:
======proc ::tcl::mathfunc::listvec {args} {return $args}
.canvas create rectangle {*}[expr {listvec($w-100, $h-40, $w+100, $h+40)}]
======

You could take it a step further, by defining expr* as follows:

======proc ::tcl::mathfunc::listvec {args} {return $args}
proc ::expr* {cargsv} {uplevel 1 "::expr {listvec([join $cargsv])}"}
.canvas create rectangle {*}[expr* {$w-100, $h-40, $w+100, $h+40}]
======
Edit: I formalized it in a Tcl package called "https://github.com/ambaker1/exprstar%|%exprstar%|%".