$Revision: 5.0.2.6 $
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
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/5.0.1.
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 functions excl:set-case-mode and excl:convert-mixed-case-symbols, 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.
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.
(The Quick Brown Fox) becomes (THE
QUICK BROWN FOX). We'll refer to this as Upper Case mode.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:
:case-insensitive-upper. In this mode readtable-case
has the effect specified in the Common Lisp spec.:case-sensitive-lower then for the purposes of
printing and reading Lisp assumes that the readtable case of all readtables is :preserve.
Lisp won't actually change the readtable-case nor will you see :preserve
returned by the readtable-case function. :case-insensitive-lower then the reader and
printer act as if the readtable-case of all readtables was set to :downcase.
Again, this forcing readtable-case to :downcase is an
internal change only.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.
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.
If portability of your code between the various case modes is important, then here are some thing you should do:
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.
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 ))
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)))
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.
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.