We can evaluate the two expression in two possible ways:
set a 1
set b 1
puts [expr $a + $b ]
puts [expr {$a + $b } ]
But why h
The "problem" with expr is that it implements its own "mini language", which includes, among other things, variable substitution (replacing those $a-s with their values) and command substitution (replacing those [command ...] things with the results of running commands), so basically the process of evaluating expr $a + $b goes like this:
expr, $a, + and $b out of the source string. Since two of these words begin with $, variable substitution takes place so really there will be expr, 1, +, and 2.expr, and executes it passing it the three arguments: 1, +, and 2.expr then concatenates all the arguments passed to it interpreting them as strings, obtaining a string 1 + 2.expr machinery, according to its own rules which include variable- and command substitutions, as already mentioned.What follows:
expr {$a + $b}, grouping provided by those curly braces inhibits interpretation by the Tcl interpreter1 of the script intended to be parsed by expr itself. This means in our toy example the expr command would see exactly one argument, $a + $b, and will perform substitutions itself."Double parsing" explained above might lead to security problems.
For example, in the following code
set a {[exec echo rm -rf $::env(HOME)]}
set b 2
expr $a + $b
The expr command will itself parse a string [exec echo rm -rf $::env(HOME)] + 2. Its evaluation will fail, but by that time, the contents of your home directory will be supposedly gone. (Note that a kind Tcler placed echo in front of rm in a later edit to my answer in an attempt to save the necks of random copypasters, so the command as written won't call rm but if you remove echo from it, it will.)
expr.1 Well, almost — "backslash+newline" sequences are still processed even inside {...} blocks.