Iverson’s Convention, or what is the value of x < y?

2007-05-25

Iverson’s Convention is, roughly speaking, the idea that a boolean result can be encoded using the first two natural numbers: 0 for false, 1 for true. More precisely that for a relationship R, aRb is 1 when a stands in the relation R to b, 0 otherwise. It’s 1 when {a,b} ∈ R, if you like to think of R a subset of the cartesian product.

Knuth promotes Iverson’s convention in this paper. Really Knuth is promoting a specific notation [aRb] meaning a function that is 1 when aRb is true and 0 otherwise. In the field of computer science I’m not really so concerned about the notation, more about the name. The name Iverson’s Convention doesn’t seem to be particularly well known, but I think it gives a useful name to distinguish different computer languages.

Computer programming languages roughly divide into those languages that use Iverson’s Convention (IC languages) and those that do not. C famously does, whereas Java does not. In C the result of «x == y» is either 0 or 1 and is of type int; in Java the result of «x == y» is either true or false and is of type boolean.

Surveying a random smattering of languages (by consulting my bookcase) with regards to the question of what a typical relational expression, like «x < y», can evaluate to I find 3 rough categories: IC languages; those that have some sort of more or less well defined boolean type; and languages that do something else.

[In the first version of this article, I placed Python in the non-IC camp. That was wrong and I have learned.]
[On 2007-08-02 I added Javascript to the discussion.]

BCPL surprised me by not being an IC language. BCPL has only one type, all values are an integer of some fixed bit-length. Iverson’s Convention would be a natural match for the language (and was adopted for one of BCPL’s descendents, C), but in BCPL true and false are implementation defined values (see [BCPL1980] page 13). FORTH is semi-IC; according to my [FORTH1983] false is 0 and true is non-zero. I expect there was a fair amount of variation across different FORTH implementations. Sinclair BASIC (on which I cut my teeth) is IC, but I remember there being considerable variation amongst different BASIC dialects.

Icon takes a completely different tack. In Icon all the usual relationship operators are control flow constructs. So «x < y» evaluates to y when the relationship holds, but when the relationship does not hold the expression is said to fail and produces no result. Constructs like if are sensitive to whether the control expression succeeded with any value or failed.

I have to say I’m surprised that their aren’t more languages in the IC camp along with C, J, and Python. Inform is esoteric but I included it because I happened to have [DM4] to hand. There are probably lots of similarly esoteric languages that I don’t know about, lazily copying the IC feature of C. Perhaps C++ is like that.

Python has an interesting take on the issue (which I got wrong in the first version of the article, but discussion below, particular from Paul and Gareth, has set me straight). In Python 2.2 expressions like «x < y» evaluate to 0 or 1 (integers). In Python 2.3 such expressions evaluate to a bool, True or False, but bool is a numeric type containing the integers 0 (spelt False) and 1 (spelt True). False and True are distinct objects from 0 and 1 but have values 0 and 1 when used as numbers. When converted to strings they convert as ‘False’ and ‘True’. It’s as if Python is trying to gradually and carefully rid itself of Iverson’s Convention.

PostScript can be thought of as a modernised FORTH, so perhaps it is an example of a language evolving from IC to non-IC (well, from nearly IC in the case of FORTH). Some languages designed since the rise of C—Java, most notably—have adopted a boolean type rather than IC. Perhaps there is a modern trend away from IC.

/bin/sh is amusing because it implements a sort of inverted Iverson’s Convention, IIC. true is 0 and false is 1, in the sense of the return codes used by programs likely to be used in if and while and friends (programs such as grep and test).

Common Lisp does have a boolean type (with values T and NIL), but generally the functions that return a boolean result return a generalized boolean where NIL is returned for false, and any other value for true. That’s why it’s in the “other” category. Another thing that sets Common Lisp slightly apart from the “boolean type” languages is that Lisp’s false value, NIL, also has another rôle for being the empty list.

Naturally, J is an IC language.

What does if accept?

That’s a snapshot of Iverson’s Convention from a viewpoint of value production. So what about the behaviour of if when its condition is some value other than the canonical true/false values (whether that be 0 or 1 in the case of IC languages, or true, false for most of the others)? Mostly the languages are quite tolerant, following the principle of being conservative in what you produce and liberal in what you accept.

All the IC languages accept any integer in a condition and treat 0 as false and non-zero as true (generalised IC). C accepts any scalar and treats the condition «x» as being the same as «x != 0». Thus in C, the float 0.0f and the null pointer also act like false, and other values act like true. awk extended conditions to strings. In awk the empty string is treated like false and any other string is treated like true (see Appendix A for an awk obscurity). perl extends false privileges to the string ‘0’, the empty list, and the undefined value.

The boolean type languages are variously liberal. They have particular canonical values for true and false. Sometimes that’s encoded in a particular type (boolean in Java, Lua), sometimes not. Java follows a strict model, only expressions that are statically of type boolean are permitted as conditions, and its boolean type contains only true and false; this is true to Java’s static heart but a spurning of its C heritage that still surprises some C to Java converts. The other languages are more dynamic and more liberal. Generally there’s a finite set of value that are treated like false (False, NIL, nil, 0) and the rest are treated like true.

Lua used to not have a boolean type; the boolean type was introduced in version 5.0; prior to that, Lua used nil (note, not the empty list) and any value different from nil as its false and true. So for historical reasons nil still acts like false when used in a condition. The caretakers of Lua think that they “should have introduced booleans from the start”, see [LUA2007] section 7.

Python follows the scripting language tradition (exemplified by awk and perl) of accepting the empty string for false and treating other strings as true; it also follows the Lisp traditional of treating the empty list (and the 0-tuple) as false.

JavaScript also follows the scripting language tradition of accepting the empty string as false, as well as null and undefined. 0 length arrays are not special (unlike Python and perl), they are true just like any other language. JavaScript is one of the few languages to incorporate IEEE 754 floating point numbers as standard; the NaNs in the floating point type are also accepted as false (as well as both zeroes: -0, +0).

So in terms of what is acceptable as a boolean there are more conventions than for production. There’s generalised IC (C, awk, Python, perl, FORTH), boolean type (Lisp, Python, Lua, Java, JavaScript), empty string (awk, Python, perl, JavaScript), empty list (Lisp, Python, perl). Python and Lua both have the convention of using a value not otherwise related to other values as being equivalent to false: None in Python, and nil in Lua (this is also similar to undefined and null in JavaScript, I’m just not sure it’s similar enough). perl has a convention not shared by any other language (that I looked at): it accepts the string ‘0’ for false.

shaurz on reddit points out that in Python objects can pass themselves off as False by implementing __nonzero__. Cool. It seems to be the only common language that makes true/false an extensible protocol.

Java is the only language that accepts exactly the same values as it produces.

There are many major languages left out, particularly given the ridiculous obscurity of some of the ones I have included, perhaps I will add more at some point (I did: on 2007-08-02 I added JavaScript). Scheme and ML deserve a showing because they Do The Right Thing; Prolog deserves a mention because, like Icon, true/false is matter of control flow, not computing values.

Appendix A – when a non-empty string in awk is true

awk is a little bit tricky because a value might have both a string part and a numeric part; when used in a condition the numeric part has priority. Where this gets hairy is for strings that would evaluate to 0 if used in a numeric context.

The first program prints a 0 followed by “hello”, the string a[1] is not empty so “hello” is printed. The second program prints just a 0. a[1] still has the same string value but this time it also has a numeric value; the numeric value of a[1] is tested by the if, found to false, so “hello” is not printed.

@Gareth: I was learning the same thing by reading the language reference as you were typing your comment. Well, could it be that making Booleans a subtype of plain integers is bonkers? (or, more tangentially, that having both plain integers and long integers is an implementation detail that should be hidden?)

So it looks like Python is IC (I’ll update the article at some point). But it looks like they’re setting it up to make Boolean not a numeric type one day.

So it is more consistent and less bonkers then I thought at first blush.

Python’s is is Lisp’s EQ. Where’s EQL? Python’s == is better than Lisp’s = because in Lisp it’s undefined to pass non-numbers to = (a fact which surprises many Lisp programmers).

Lisp extends itself the privilege of allowing (EQ 3 3) to be NIL. Obviously this seems silly for a number like 3, but it starts to seem reasonable when 3 could in fact be some huge bignum possibly reached via macro expansions or other computations. Lisp doesn’t distinguish between Plain integers and Long integers the way Python does, they’re all just integer.

Python is coy on the matter of whether x is x can be False when x is a Plain integer. The python lang spec clearly states that you’re likely to get different Long integer objects with the same value, and it clearly states that there are only 2 Boolean objects (so True is True is guaranteed to be True), but it simply doesn’t say when an implementation can have two Plain integer objects with the same value.

Implementation says Plain integer objects can be copied, so we can replicate your results using Plain integers.

Hmm, I don’t think you would need words longer than 32 bits to create a tagged implementation of Python because the semantics of Plain integer as described in the language specification don’t say anything about boxes.

As far as I can tell, the only requirements on Plain integers are: operations on them that result in values in the range of Plain integers yield Plain integer objects; type on Plain integer objects returns the Plain integer type object.

In terms of implementation, some Plain integers would be immediate and some would be boxed.

Plain integers act just like Long integers. The only way you tell the difference is if you use type or notice the L when you convert them to strings.

Historical note: 1<<32 and 1L<<32 used to be different (in Python 2.3), but they fixed that (in Python 2.4).

Which is really more or less how C++ does it; there is a distinct boolean type, but it can be silently converted to an integer value. In Python you can use True and False as synonyms for 1 and 0 resp.

[drj: I’ll reply here instead of in a seperate comment: Thanks for the comment Jack, it’s essentially the same point Paul made earlier and I’ve edited the article to reflect my new understanding of Python]

Haskell has a separate type Bool that can only be True and False. You can’t do arithmetic immediately with it, but it is possible to add that capability with a few lines. Interconversion to integers happens explicitly with “fromEnum” and “toEnum”. Implicit conversion can be added fairly quickly as well. “” do work.

Pascal is a boolean language, which is an enum, which is a ordinal value. ORD maps an ordinal value onto an integer.
Type
Boolean = (False,True)
var i: integer;
begin
i:=ord(true);
i:=ord(false);
So if Pascal is IC or Boolean depends on how you judge the ORD “function” (it is not a cast nor a real function. It is a language built in type conversion).
If you decide its a special conversion, it is boolean.
If you decide it is just a result of an attempt at a more coherent and safer ordinal typesystem, then it is IC, and you have:
if ord( x < y ) = 1 then
writeln(‘stupid html’);

A small addition, both booleans and integers are a subset of ordinals (which are roughly “types having a fully compiletime ordering”).

I also forgot to add my opinion. I go for BOOLEAN, but the case that boolean and IC’s categories are distinct enough to matter is IMHO severely weakened by it. It shows that math notation and languages have a lot of similarities, but also a lot of differences.

I wouldn’t be surprised if you dive deep enough into the other languages types, you find similar problems.

You write: “It’s as if Python is trying to gradually and carefully rid itself of Iverson’s Convention.”

I don’t think this is the case. In the PEP introducing the boolean type ( http://www.python.org/dev/peps/pep-0285/ ), one of Guido’s arguments in favor of it was that making 0 mean False and 1 mean True can confuse beginners, because sometimes 0 just means 0 and 1 just means 1. His example was the cmp function, which takes two arguments x and y and returns zero if they are equal, positive if x is larger, negative if y is larger. (Java has something similar.) For the built-in types, cmp returns a number in (-1, 0, 1); this invites comparison with the range (0, 1) of booleans, and this resemblance is misleading.

Interesting, thanks for the PEP. I think Guido is right, using 0 for False and 1 for True probably can confuse beginners. But in Python, 0 and False are ==, as are 1 and True. So all that’s really happening is that there’s a different spelling for 0 and 1. Should «True+1» be illegal (in the future)? Not according to Guido.

I guess this is all consistent with Python’s philosophy which seems to be something like: if you want to treat a random object as a number then go right ahead, maybe some sensible computation will result. Or maybe not.

This is a result of python’s typing system. In a sense True and False can be treated as integers because of their contextual dynamic type conversion, which is also coined “duck typing”. Nevertheless the bool type in python is a straightforward subclass of int:

>>> issubclass(bool, int)
True

To conclude that True == 1 being True means that booleans are treated as integers is a bit misleading. On the other hand you can use 1 as True easily, but this results from python interpreting anything non-0 or non-emtpy (basically anything non-false) as being True…

Your last Python snippet is a perfect example of the fact that True is the integer 1 (note: when I say integer I mean the mathematical integer and when I say int I mean the python type).

~True is just the same as ~1. It’s -2 because ~ acts as if Python uses 2’s complement arithmetic (which is almost certainly does underneath, but even if the underlying hardware didn’t the language spec says that Python acts as if it uses 2’s complement).

~1 is 0b1111111…11110 (or would be if Python had binary literals… and allowed them to be infinitely long). This is the binary representation of -2 in 2’s complement.

In general for a 2’s complement representation ~x == -x-1 :

>>> print ~True, -True-1, ~1, -1-1
-2 -2 -2 -2

Do you really think that True is not a number? Isn’t bool being a subclass of int also a clue to the fact that True is a number? Not only is it a number it’s an int (because it’s a bool).

By the way, stuff like «This is equivalent to if “” == True» is dangerous nonsense because it fails for your next example. «”x” == True» is False (of course), but your «if “x”» example prints True.

bool can’t be subclassed but int can. Fascinating, I wonder what the justifcation of that is?

Regarding BCPL, I wonder if the 1980 book was being excessively careful about portability. The current spec guarantees that TRUE is -1 and FALSE is 0. All-ones for true makes sense from a low-level perspective, and makes it safer to use bitwise operators as boolean connectives.

Could you call it the two’s complement of Iverson’s convention?

BBC BASIC inherited various stuff from BCPL, most obviously the indirection operators. I expect it also copied the truth value, if this is a departure from other BASICs.

In comment 2 of this article (just after NickB’s comment saying that BBC BASIC used -1 for FALSE), I humorously call it “Negated Iverson’s Convention”. But yeah, 2’s Complement Iverson’s Convention is more accurate. 2CIC, mmm.

What’s ironic in this discussion is that from a mathematical viewpoint “Boolean” results have always been integer results.

Once upon a time, there was a guy named George Boole. He had something to do with a form of mathematics that some people called a “Boolean Ring” and there was a variation that some people called “Boolean Algebra”.

And then, someone decided to change the names of things.

So now what was once a “Boolean Ring” is now called a “Boolean Rng” (and I have no idea how to pronounce that, so maybe “called” is the wrong way to describe this state of being). And, what was once called a “Boolean Algebra” is now called a “Boolean Ring”.

(Though I suppose it could be argued that coming up with an example of a “Boolean Rng” which is not a “Boolean Ring” is difficult — and it might involve what a computer programmer might describe as a parent type which is not an integer. Still, from the viewpoint of the definitions this kind of switcharoo on the names makes talking about the names a bit confusing.)

And what was once called a “Boolean Algebra with identity” (of which there are infinitely many) is now called a “Boolean Algebra” (and there appears to be only one of those).

So… anyways… people reflexively say “a boolean is not an integer” which is sad, because an original point of using the term “boolean” instead of “logical truth value” was that booleans are, for most all practical purposes, integers.

Of course, a computer type person might also say that an integer is not a real number. But from a mathematical point of view you cannot find any examples of an integer which is not also a real number.

I suspect that we are losing track of our concepts because of our focus on the containers we are using to represent some of them (and I suppose that this includes that renaming stuff, which can pull the rug out from under someone trying to read an older mathematical text). I think that this says something bad about the utility of those containers.

>> A variable is classified according to the range of values it may assume: it is logical, integral, or numerical, according as the range is the set of logical variables (that is, 0 and 1), the set of integers, or the set of real numbers.
Each of the foregoing classes is clearly a subclass of each class following it, and any operation defined on a class clearly applies to any of its subclasses.

and:

>> More generally, if a and b are arbitrary entities and R is any relation defined on them, the relational statement (aRb) is a logical variable which is true (equal to 1) if and only if a stands in the relation R to b. For example, if x is any real number, then the function (x>0)-(x0)-(x<0)).
The relational statement is a useful generalization of the Kronecker delta, that is, delta_i,j = (i=j).

For example, if x is any real number, then the function (x>0)-(x<0) (commonly called the sign function or sgn x) assumes the values 1, 0, or -1 according as x is strictly positive, 0, or strictly negative. Moreover, the magnitude function |x| may be defined as |x| = x * sgn x = x * ((x>0)-(x<0)).

So given that APL introduced IC it should be on the IC list. On the other hand, Iverson did not suggest any convention to convert arbitrary integers to truth values (the ‘what does if accept’ part) and I think APL is restrictive here (as is J), so it would not be on the generalized IC list.

Anyway, very good idea to write about this important concept and to help popularizing the name!

There is no “generalized ic” here list that I can see, so that’s a false concept.

In fact, given that “ic” stands for “Iverson’s Convention” and given that Iverson was the primary author of APL, I’d expect that APL rather accurately matches Iverson’s thinking on the topic.

Note also that using 0 and 1 for truth values works well for dealing with probability. (0.5 would express even odds, for example, logical negation is equivalent to subtracting the truth value from one. Also logical multiplication is the same thing as arithmetic multiplication.)

Thus this approach is also extremely general in the sense of being useful.