Artifact [5179351b4d]

Login

Artifact 5179351b4d940983ea3cb625f354e180d6149c9d998bf29b2a080937194e9131:


TIP:            182
Title:          Add [expr bool] Math Function
Version:        $Revision: 1.16 $
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

~ 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.