Updated 2011-01-11 11:21:09 by Stu

How do you create constants in Tcl programs? There are, of course, several ways.

LV: Well, one way to create a constant in a Tcl program is to just code it. For instance, in the line of code:
`        set a "abc"`

The string "abc" is a constant.

Note, in fact, that the string need not be in quotes:
`        set b abc`

also involves a constant string "abc".

Well, a decimal value can be set thusly:
`        set c 10`

or
`        set d "10"`

and, when \$c or \$d are used in Tcl commands which expect numeric values, the value of the variable will be treated as the number 10.

Note that Tcl versions up through 8.4.1 treat a leading 0 as indicating that the numeric value is octal:
`        set e "010"`

does not result in \$e being equal to \$d; instead, it is treated as being two less than \$d.

Are there ways to express numeric constants in other bases, such as binary or hexadecimal?
``` \$ tclsh
% incr a
2990
% ```

So it appears that, with appropriate notation, one can get hexadecimal.

For binary, one needs to do this:
```        proc fromBinary { digitString } {
set r 0
foreach d [split \$digitString {}] {
incr r \$r
incr r \$d
}
return \$r
}
puts [fromBinary 0101001010101010]
puts [format %x [fromBinary 0101001010101010]]```

or
```        proc bits2int {bits} {
#returns integer equivalent of a bitlist
set bits [format %032s [join \$bits {}]]
binary scan [binary format B* \$bits] I1 x
set x
}```

However, what if you want to create variables which, once set, can be guaranteed to retain their value? Obviously the guarantee is conditional, since in many/most cases, something that one can script in Tcl is going to be able to be unscripted with sufficient effort...

procedure constants

Create a procedure for each constant, which returns the value.
`  proc FLAG1 {} { return 0x0001 }`

You then access the constant by calling the procedure, like [FLAG1]. This is easy to code, but gets clumsy when you have lots of constants. Another possibility is to use an array in one constant function, but this is fairly inflexible.
```  proc CONST { key } {
array set constant {
FLAG1  0x001
FLAG2  0x002
PI  3.14159
}
return \$constant(\$key)
}```

Brent Welch suggests using write variable traces to implement read-only variables. But since the write trace fires after the variable value has changed, you need to keep a cache of the original value somewhere.

George Howlett made a suggestion (at a Tcl conference tutorial) that read traces are your friend. He suggested a couple of very flexible procedures. (According to Don Porter's c.l.t. post)
```  % proc _constant_read_trace {val name1 name2 ops} {
upvar \$name1 var
set var \$val
}
% proc constant {varName value} {
uplevel [list trace variable \$varName r [list _constant_read_trace \$value]]
}
% constant PI 3.14159
% set PI
3.14159
% set PI 3; # Only in Indiana :)
3
% set PI
3.14159```

However, note the following:
```  % set v [set PI 3]
3
% puts \$v
3```

Thus, while PI continues to have a constant value, note that the result from the set appears as if PI had been given a different value. So one needs to be careful how one plans on using this code.

RS: Slightly modified the above, so attempts to vary a constant raise an error (it probably was one ;-):
```  proc _constant_trace {val name1 name2 ops} {
upvar \$name1 var
if {\$ops=="w"} {
return -code error "constant \$val may not be changed"
}
set var \$val
}
proc constant {varName value} {
uplevel [list trace variable \$varName rw [list _constant_trace \$value]]
}
% set PI 1.23
can't set "PI": constant 3.14159 may not be changed```

RS again: This raises no error, but keeps a constant with minimal code:
``` proc const {name value} {
uplevel 1 [list set \$name \$value]
uplevel 1 [list trace var \$name w "set \$name [list \$value];#" ]
}```

Simple error-raising variation:
``` proc const {name value} {
uplevel 1 [list set \$name \$value]
uplevel 1 [list trace var \$name w {error constant ;#} ]
}
% const x 11
% incr x
can't set "x": constant```

kruzalex This prevents constant without raising an error too:
``` rename set _set

proc set {var args} {
if {[llength \$args]!=0} {
uplevel 1 [list _set \$var \$args]
} else {
return [uplevel 1 [list _set \$var]]
}
}

proc _constant_read_trace {val name1 name2 ops} {
upvar \$name1 var
uplevel 1 [list _set \$name1 \$val]
}

proc constant {varName value} {
uplevel [list trace variable \$varName r [list _constant_read_trace \$value]]
}

constant PI 3.14159
puts #step1
puts [set PI]
set PI 3; # Only in Indiana :)
puts #step2
puts [set PI]
puts #step3
set some [set PI 3]
puts [set PI]
puts "some: \$some"
puts #step4
set some 4
puts "some: \$some"
puts #step5
set some [set PI]
puts "some: \$some"```

RS: From a comp.lang.tcl post, this variation is not about preventing changes, but to import defined "constants" in proc scope:
``` interp alias {} define {} lappend ::defines

proc use_defines {} {
foreach {key val} \$::defines {uplevel 1 [list set \$key \$val]}
}
#-- Test and demo:
define PI 3.14
define e 2.781

proc try {} {
use_defines
return "PI=\$PI, e=\$e"
}
% try
PI=3.14, e=2.781 ```

Karl Lehenbauer, I think, gets credit for one invention of read-only variables [cite references].
``` #By George Peter Staplin

set ::constants [list]

proc constant {name value} {
global constants

lappend constants \$name \$value
}

proc constproc {name argpat body} {
global constants

proc \$name \$argpat [string map \$constants \$body]
}

constant PI 3.14159
constant PROCESS [pid]
constant FLAG1 2
constant FLAG2 4
constant FLAG3 8

proc & {a b} {
expr {\$a & \$b}
}
constproc test {} {
puts "PI PROCESS"

set n 107

puts "[& \$n FLAG1] [& \$n FLAG2] [& \$n FLAG3]"
}

test```

NEM notes that command names are the natural choice for constants:

• they live in a separate namespace to variables;
• they are rarely redefined, and few commands do so;
• global commands are available everywhere without importing;
• they offer the possibility of byte-code optimisation, i.e. inlining (no idea if this is or could be done).
``` proc def {name = args} {
interp alias {} \$name {} const [expr \$args]
}
proc const a { return \$a }
def PI      = acos(-1)
def PROCESS = [pid]
def FLAG1   = 0x01
def FLAG2   = 0x02
def FLAG3   = [FLAG1] | [FLAG2]
proc test {} {
puts "[PI] [PROCESS]"
puts "FLAG3 = [FLAG3]"
}
test```

wdb Good idea ... when making a function, why not making it even more handy?
``` proc pi {args} [subst -novariable {
expr [expr {atan2(0,-1)}] \$args
}]```

Now, use it:
``` % pi
3.14159265359
% pi / 2
1.5707963268```

Stu 2011-01-11 On similar lines, I came up with this recently.
```proc aliasconst {name val args} {
if {[llength \$args] % 2 != 0} { error "must be %2!" }
foreach {n v} [linsert \$args 0 \$name \$val] {
interp alias {} \$n {} return -level 0 \$v
}
}

% aliasconst ^Z \x1a ^J \n me Stu
% me
Stu```

See also Tcl and octal numbers. and Binary representation of numbers. Some important constants are available from the tcllib math::constants package.

male - 2007-07-26 - My two cent on [const package]

math::constants