Allegro CL Case Modes

$Revision: 5.0.2.3 $

The document introduction.htm provides an overview of the Allegro CL documentation with links to all major documents. The document index.htm is an index with pointers to every documented object (operators, variables, etc.) The revision number of this document is below the title. These documents may be revised from time to time between releases.

1.0 Introduction to case in Allegro CL
2.0 The Modes
3.0 readtable-case
4.0 The cost of portability
5.0 Portability
   5.1 Package names
   5.2 Package definitions
   5.3 Creating symbols
   5.4 Testing symbols
6.0 Importing Upper Case code into Lower Case mode

1.0 Introduction to case in Allegro CL

In standard Common Lisp, the reader converts all unescaped lowercase characters to uppercase, so that for example Foo and foo are both read as FOO. The fact that Upper Case mode exists shows how long Lisp has been around. When Lisp started there were primarily upper case input devices. When lower case capability started to appear, the simplest way to handle those lower case characters was to simply convert them to upper case. This conversion didn't matter since everything worth naming was case insensitive as well, such as file names and names of library functions written in Fortran or assembler.

Technology marches on and eventually upper-case-only input devices were retired. The Unix operating system spread all over the world and introduced a filesystem and programming language that are case-sensitive with lower case being the normal case for names. Upper Case mode is very inconvenient for naming objects in the Unix world. Thus Lower Case mode was introduced, first popularized in Franz Lisp and then in Allegro Common Lisp. The Windows operating system has even gone further in naming objects in a case dependent matter, with nearly every API function using dual cases (e.g. SetWindowTitle).

Creating a new mode for Lisp would be detrimental if the result was that it fractioned the Lisp world. Fortunately this wasn't the case. It turned out to be very easy to write programs that could work in either mode. At the end of this document we will show the coding style that results in programs portable between the modes. Allegro Common Lisp is itself a good example of a large Lisp program that can work in any case mode.

There are two parameters that determine the reader's actions: case preference and case sensitivity. The preferred case is either upper or lower, and refers to the case of the characters in the print names of all of the standard symbols, such as car and cdr. Case sensitivity can be either sensitive or insensitive to case. Case-sensitive means that the Lisp reader does not modify the case of any characters it reads. Case-insensitive means that characters that are not of the preferred case are converted to the preferred case.

Thus, there are four possible values for the combination of case preference and case sensitivity. However, only three are supported in Allegro CL 5.0.

Case-insensitive, uppercase-preferred. This is the mode used in standard Common Lisp and in most of the older Lisps such as MacLisp. With this mode you can even enter Lisp programs with a card punch. This is the most portable mode.

Case-sensitive, uppercase-preferred. This is the mode used by InterLisp and the mode that Allegro CL no longer supports.

Case-insensitive, lowercase-preferred. This mode is similar to the case-insensitive, uppercase-preferred mode. This mode is useful if you are reading files designed for a case-insensitive Lisp into a case-sensitive, lowercase preferred Lisp.

Case-sensitive, lowercase-preferred. This is the mode used by many modern languages, including, for example, the C programming language. It matches the conventions of Unix and thus is the most natural mode to use for some programmers.

The function excl:set-case-mode and two variables excl:*current-case-mode* and excl:*ignore-package-name-case* are provided for controlling and sensing case modes.

So, Allegro Common Lisp can operate in three different case modes. In this document we will go into more detail on the two important modes and describe their advantages and disadvantages. We'll show how to write code that works in any mode.

2.0 The Modes

There are three supported modes. :case-sensitive-lower and :case-insensitive-upper are the primary modes. The third, :case-insensitive-lower is useful for porting code between the two primary modes.

3.0 readtable-case

Late in the standardization of Common Lisp the designers realized the case sensitivity was important to Lisp users. At this point the rest of computing world was in Lower Case mode using case sensitive filenames and function names with lower case being the preferred mode. Rather than switch Common Lisp to Lower Case mode they chose instead to add a readtable attribute called readtable-case. The function cl:readtable-case accesses (and with setf sets) the readtable-case of a readtable object.   When readtable-case is set to :invert, case is preserved except that the case is inverted for symbols whose names are all the same case. Thus given a string you can't tell how to present it to the user unless you know where the string came from and thus can tell if it's a normal or an inverted symbol-name string. We'll use the term Inverted mode to describe the situation when readtable-case is :invert.

If you chose to use readtable-case in your program then you should not use set-case-mode as well. They are two independent methods for altering the reader and printer behavior and in order to keep them from conflicting with each other we've done the following:

At this point you should decide if you want to achieve case sensitivity by using the :invert value for readtable-case (and thus having to work with some case inverted strings), or by using set-case-mode. If you decide to use readtable-case then you can stop reading this document. If you want to use set-case-mode then read on.

4.0 The cost of portability

Before describing how to make code case-mode portable we will examine the cost of such portability. If you're working in Upper Case mode then there is little to lose by keeping the code portable. You only have to remember a few coding conventions and you can share your code with your friends who use Lower Case mode.

If you like to work in Lower Case mode then portability to Upper Case Mode may cost you the ability to use the full expressibility of the Lower Case mode. For example you may want to use the convention that your Clos class names are capitalized. Thus you might have a class Box and set the value of the symbol Box to the Box class object. You might also have another symbol named box whose value is an instance of a the class Box. This will work fine in Lower Case mode. However when you try reading this code in Upper Case mode the symbols Box and box will both end up being the same symbol whose symbol-name is "BOX".

You could port more easily from Lower Case mode to Inverted Mode however that relies on the target of the port being able to deal with an Inverted Mode module. Most projects seem to be in either Lower Case mode or Upper Case mode.

Lower Case mode gives you a lot more expressibility than Upper Case mode. If you use Lower Case mode and are concerned about porting your code to Upper Case mode then you must constrain yourself to what Upper Case mode can support. This is the cost of portability.

5.0 Portability

If portability of your code between the various case modes is important, then here are some thing you should do:

5.1 Package names

Packages are named by strings and are looked up in a case sensitive manner. In Lower Case mode the standard package names are in all lower case and in Upper Case mode they are in upper case. Thus a good way to refer to package is not by a string e.g. "common-lisp-user" but by a symbol such as :common-lisp-user or #:common-lisp-user. Putting the symbol in the keyword package or using an uninterned-symbol keeps from cluttering the current package (and maybe causing symbol clashes).

Symbols can be used nearly everywhere a package name is needed. The functions that look for package names use the symbol-name of a symbol passed as an argument. By doing

(in-package :ole)

you end up calling in-package with "ole" in Lower Case mode or "OLE" in Upper Case mode, and thus the package is found regardless of the mode.

A global solution to matching package names is to set the variable *ignore-package-name-case*. When the value of that variable is true, package name to package lookup is done in a case insensitive manner.

5.2 Package definitions

In package definitions you also want to use symbols in place of strings, as in:

(defpackage :foreign-functions 
      (:nicknames :ff) 
      (:use :common-lisp :excl) 
      (:export #:def-foreign-call 
               #:def-foreign-type
               #:defun-foreign-callable 
               #:def-foreign-variable ))

5.3 Creating symbols

The most common non-portable form we come across looks like this

(defun constructor (struct) (intern (format nil "MAKE-~a" struct)))

This assumes that this code will run in Upper Case mode. Writing it as this allows the case of the "make-" to be determined by the case of the :make- symbol's printname, which will be correct for the case mode:

(defun constructor (struct) (intern (format nil "~a~a" :make- struct)))

5.4 Testing symbols

When reading and testing against symbol names you'll want the code to work regardless of the case of the print names. This can be accomplished by using case-insensitive predicates (such as equalp). Another possibility is to use (string :foo) rather than "foo" or "FOO" so that the correct string for the current case mode is used at runtime.

6.0 Importing Upper Case code into Lower Case mode

Generally Upper Case mode code can be made portable by looking for and fixing the few items we've mentioned above. However you may encounter code written in a style such as this:

(DeFun Foo (x y)
      (Cond ((PlusP x) (Sqrt y)) 
            (t y)))

The author of this code is taking advantage of the fact that in Upper Case mode the case of symbols in the source code doesn't matter. In order to port this to Lower Case mode you could go through and lowercase every symbol naming an official Lisp form. The author of the code is unlikely to want the code back after such a transformation, as he must see some value to his style of capitalization. A way to make this code readable into Lower Case lisp without modifying it is to switch the lisp to :case-insensitive-lower mode. In this mode, the strange symbol names will be downcased. After reading in this file the mode can be switched back to :case-sensitive-lower mode.

Note that when you switch to :case-insensitive-lower mode the value of cl:*print-case* determines how symbols will be printed. The initial value of cl:*print-case* is :upcase. Thus unless you change cl:*print-case* you'll find that when you switch to :case-insensitive-lower, symbols will start printing in upper case even though their print names are in lower case. If this annoys you, you can set cl:*print-case* to :downcase.

Copyright (C) 1998-1999, Franz Inc., Berkeley, CA. All Rights Reserved.