In array languages, Boolean data consists only of the numbers 0 and 1 (outside of APL, Boolean data may use any two values). Boolean values are used in APL whenever a "true" or "false" value is required, that is, the truth value of a logical proposition such as a comparison of two scalars. The identification of "true" with 1 and "false" with 0 is a simplification of the Iverson bracket, a notation which allowed a truth value to be converted into a number. A notable feature of APL implementations in relation to other language families is their tendency to use bit Booleans (eight in each byte) rather than byte Booleans, a feature which is difficult to implement but increases storage density and can also greatly speed up computation on Boolean arrays.
The result of a comparison such as Less Than is always composed of Booleans. Because Booleans are numbers, arithmetic functions can be used on them directly, for example by summing a result vector to find out how many times the comparison was true:
3 1 4 1 5 9 2 < 5 1 1 1 1 0 0 1 +/ 3 1 4 1 5 9 2 < 5 5
⎕IO←0 '-+'[3 1 4 1 5 9 2 < 5] ++++--+
The use of numbers for Booleans also allows functions to be extended from a Boolean domain to a more general domain. The function Compress was originally defined to take a Boolean left argument and use it as a filter for the right argument (removing right argument values corresponding to 0), but has generally now been replaced by an extension called Replicate which can copy right argument values multiple times.
1 1 1 1 0 0 1 / '3141592' 31412 0 4 1 2 0 0 0 / '3141592' 1111411
A monadic or dyadic function (sometimes scalar function) which has a Boolean result when passed Boolean arguments is called a Boolean function. Two Boolean functions are considered the same if they have the same result for every Boolean input. Since there are a finite number of possible inputs (two for monadic functions and four for dyadic functions), and only two results, there are only a finite number of Boolean functions: four monadic and sixteen dyadic.
|1||0||1||0||Right argument negation|
|1||1||0||0||Left argument negation|
Optimizing for Boolean arrays is one special case of using a small internal type in order to reduce memory usage and improve speed in an APL interpreter. For Booleans this optimization is particularly valuable because each Boolean is representable with a single bit—the smallest discrete quantity available on a CPU. The decision to use packed arrays with eight Booleans for each byte was made by Larry Breed in APL\360, even though the IBM System/360 did not have the capability to address individual bits. Later APLs implemented with bit Booleans include APL\3000, SHARP APL, Dyalog APL, and dzaima/APL.
Packed Booleans are especially valuable in array languages because only arrays of Booleans are subject to this optimization: a single Boolean in a processor will use an entire register just like an integer or floating-point value. Using a packed representation for arrays of Booleans both decreases memory usage by a factor of eight and makes faster algorithms possible, as eight times more one-bit values may be fit in a single CPU register (and thus manipulated at once) than one-byte values. However, packed Boolean arrays have a drawback: while optimized algorithms on bit Booleans are usually faster than those on byte Booleans, packed Boolean algorithms are often considerably harder to implement, and unoptimized algorithms using bit Booleans tend to be slower. This is because bits in memory cannot be addressed directly on a modern computing architecture. To read or write a single bit at an arbitrary offset, the byte containing it must be read and then the appropriate bit picked out.
Boolean algorithms are a frequent topic of discussion for APL implementors at the Dyalog user meeting. The following talks all focus primarily on Boolean algorithms:
- Dyalog '08 (05): Roger Hui, "Performance Improvements in Dyalog: A Case Study"
- Dyalog '16 U08: Bob Bernecky, "A Compendium of SIMD Boolean Array Algorithms in APL"
- Dyalog '17 D08: Marshall Lochbaum, "Moving Bits Faster in Dyalog 16.0"
- Dyalog Blog: Marshall Lochbaum, "Expanding Bits in Shrinking Time"
Because they are so small relative to CPU registers, Booleans can make SIMD (Single Instruction Multiple Data) techniques viable even in processors with no dedicated SIMD unit. Programming in this way is called SWAR (SIMD Within a Register), and is considerably easier when working with bit Booleans than when working with integers. While most processors now have SIMD registers and instructions, current SIMD implementations don't provide all the bitwise operations that are available for ordinary registers, such as addition or bitwise shifts on entire registers. Boolean SWAR programming can be useful to more easily develop algorithms which are fast (often beating dedicated SIMD optimizations with each Boolean stored in a byte) while remaining architecture independent.
- Bob Smith, "Boolean Functions"
- Jonathan Barman, APL and Partitioned Data (a follow-up on Smith's paper)
- Eugene McDonnell, "The Caret and the Stick Functions" (on classifying Boolean functions).
- Phil Last, "Boolean Reductions"
- John Scholes, "Data-driven Conditionals"
|APL features |
|Built-ins||Primitives (functions, operators) ∙ Quad name|
|Array model||Shape ∙ Rank ∙ Depth ∙ Bound ∙ Index (Indexing) ∙ Axis ∙ Ravel ∙ Ravel order ∙ Element ∙ Scalar ∙ Vector ∙ Matrix ∙ Simple scalar ∙ Simple array ∙ Nested array ∙ Cell ∙ Major cell ∙ Subarray ∙ Empty array ∙ Prototype|
|Data types||Number (Boolean, Complex number) ∙ Character (String) ∙ Box ∙ Namespace ∙ Function array|
|Concepts and paradigms||Conformability (Scalar extension, Leading axis agreement) ∙ Scalar function (Pervasion) ∙ Identity element ∙ Complex floor ∙ Total array ordering ∙ Tacit programming (Function composition, Close composition) ∙ Glyph|
|Errors||LIMIT ERROR ∙ RANK ERROR ∙ SYNTAX ERROR ∙ DOMAIN ERROR ∙ LENGTH ERROR ∙ INDEX ERROR ∙ VALUE ERROR|