TIP 182: Add [expr bool] Math Function

Login
Author:         Joe Mistachkin <[email protected]>
Author:         Don Porter <[email protected]>
State:          Final
Type:           Project
Vote:           Done
Created:        23-Mar-2004
Post-History:   
Tcl-Version:    8.5
Tcl-Ticket:     1165062

Abstract

This TIP proposes a new expr math function bool().

Background

Several of the Tcl/Tk built-in commands make use of the notion of a Boolean value, a value that can be either true or false. Boolean values show up in control commands like if and while, and they also are used to enable/disable certain features, as in tcltest::singleProcess $bool and namespace ensemble configure $ens -prefixes $bool.

There have long been two distinct notions of the set of string values that are recognized as boolean values.

The Tcl_GetBoolean() routine recognizes the following strings and their unique prefixes, in all case variations, and nothing else as valid boolean values: 0, 1, yes, no, true, false, on, off. Several script-level commands are implemented by calls to Tcl_GetBoolean, and also recognize only this limited set of string values as boolean values. Examples include string is boolean, string is true, string is false, and fconfigure $chan -blocking. Examples from Tk include configuration options of the canvas, text, and scrollbar that expect boolean values.

The Tcl_ExprBoolean() routine interprets the result of an expression evaluation as a boolean. It recognizes all the values recognized by Tcl_GetBoolean(), but it also recognizes all numeric values as booleans as well. Any string which Tcl can interpret as any kind of number is a boolean according to Tcl_ExprBoolean() with the non-zero numbers seen as true, and others seen as false. Script-level commands such as if and while have adopted this view of booleans, so a script like while {[incr x -1]} {} works as expected. It also means the script if 0x1 {} is perfectly acceptable, even though string is boolean 0x1 reports that 0x1 is not a boolean.

The Tcl_GetBooleanFromObj() routine arrived in Tcl 8.0, and contrary to what its name might suggest, it was and is not an Obj-ified form of Tcl_GetBoolean. Instead, Tcl_GetBooleanFromObj() adopted the Tcl_ExprBoolean understanding of what was and was not a boolean value. Over time as more Tcl and Tk commands were Obj-ified, it was natural to replace Tcl_GetBoolean calls with Tcl_GetBooleanFromObj and in the process several commands have come to accept a broader class of boolean values than they once did. For example,

 % info patch
 7.6p2
 % clock format 0 -gmt 0x1
 expected boolean value but got "0x1"

 % info patch
 8.0.5
 % clock format 0 -gmt 0x1
 Thu Jan 01 00:00:00 GMT 1970

Another (accidental?) change in behavior in Tcl_LinkVar() sneaked in with the [72] changes between Tcl 8.3 and Tcl 8.4. A linked variable of type TCL_LINK_BOOLEAN now allows the Tcl variable to hold any numeric value, while in pre-8.4 releases of Tcl, only those values acceptable to Tcl_GetBoolean were permitted.

Tcl's built-in math functions include three that are devoted to "casting" operations, int(), double(), and wide(). In each case, the result of the function is a string that passes the corresponding string is test. For example,

 string is integer [expr {int($x)}]

will either return 1, or raise an error; it will never return 0.

Proposed Change

Add a new built-in unary math function bool() that accepts all values accepted by Tcl_GetBooleanFromObj() and returns one of the boolean values 0 or 1, which are acceptable to Tcl_GetBoolean.

With that definition in place,

 string is boolean [expr {bool($x)}]

will either return 1, or raise an error; it will never return 0.

Reference Implementation

A "quick and dirty" implementation would be (see [232]):

 proc ::tcl::matchfunc::bool x {expr {!!$x}}

A preferred implementation is at Tcl Patch 1165062. http://sourceforge.net/support/tracker.php?aid=1165062 The preferred implementation has somewhat nicer error message reporting, and has greater potential for bytecode performance improvements.

Rationale

First, this simply makes a nice parallel with the existing "casting" functions. It does away with the surprise expressed by some that a language making use of doubles, integers, and booleans provides double() and int(), but not bool().

Second, because we have this bifurcated opinion about what values really count as boolean values, the proposed math function provides to the script level a way to safely accept booleans in the broader sense, even if using an interface that may be narrow.

 fconfigure $chan -blocking [expr {bool($value)}]

This is, of course, equivalent to

 fconfigure $chan -blocking [expr {!!$value}]

but should be easier on the code-reading eyes.

Copyright

This document has been placed in the public domain.