Recent support questions of general interest

Documenting a large and complicated product like Allegro CL is never easy and even conscientious users often have difficulty finding answers to specific questions in the documentation. In this note, we flag some recent support questions which might be of general interest. They are all from 2013 and 2014.

Updating Allegro CL with on a machine with no internet access

Allegro CL has various tools for updating, that is getting the latest patches. You can use sys:update-allegro from within Lisp or Unix) or update.exe (on Windows). All these tools take care of downloading all patches, and the update programs additionally rebuild the images. But all depend on internet access for downloading the pacthes.

A customer asked about what to do if the machine running Allegro CL does not have internet access (usually because of security concerns). There is no general answer to this question because important details are unknown, but here is a method that will work as long as there is access to a similar machine that does have internet access and can transfer files to the isolated machine (by, for example, using a removable disk):

  1. On the isolated machine, make sure you never write any files into the allegro directory. It should be the initial distribution, the license, and any updates. This means the directory can be removed and replaced without affecting any of your own files.
  2. Have a copy of the distribution on an equivalent internet available machine. This copy should use the same license. (This is a valid use of the license so long as the copy is not improperly used for other purposes.)
  3. Use the distribution in 2 to get updates and run
  4. On the isolated machine, rename the Allegro directory (from acl90/ to, say, acl90-old090114).
  5. After an update (including running on the internet-enabled machine, bundle the Allegro directory up and get it to the isolated machine.

Now the isolated machine has an updated version. All PATHs etc. should work as before as the Allegro directory name is unchanged. And, if there is a problem with the update, reverting to the version previous to the update can be done simply by renaming directories.

Sometimes you can shoot yourself in the foot

A customer reported code similar to this, when evaluated, caused Allegro CL to crash:

(defclass foo ()
  ((sl1 :accessor sl1 :initarg :sl1 :initform "" )
   (sl2 :accessor sl2 :initarg :sl1 :initform "" )
   (bar :reader bar :initform "" )

(defmethod initialize-instance :after (instance &rest initargs)
  (setf (slot-value instance 'bar) 
        (format nil "~a~a" (sl1 instance) (sl2 instance))))

The problem is, as the customer had already recognized, that the initialize-instance method was not speciailized, as inteneded, to instances of foo. Therefore it was being called when any instance of any class was being created. Since most classes do not have a slot named bar, the system tries to signal an error but cannot even do that since error also does not have a slot named bar. The customer was not looking for programming guidance, but was instead wondering why errors of this kind are not protect against.

And we agree that it would be good if this was protected against (and intend to enhance to package lock facility to cover this case (see here for information on package locking). To see package locking in action, try redefining format:

cg-user(8): (defun format (&rest args) nil)
Error: Attempt to make a function definition for the name format.
   This name is in the common-lisp package and redefining it is a
   violation for portable programs.  Replacing the current
   definition of format may be dangerous.  The package
   common-lisp has package-definition-lock set, which causes the
   system to signal this violation.

[condition type: package-locked-error]

This note is just a reminder to users that the flexibility of Common Lisp does allow you modify the system in ways that make it impossible for Lisp to continue. We try to protect against such errors but some cases fall through the cracks.

Why can't pprint-newline return t when it does print a newline?

A customer asked why pprint-newline (which prints a newline to a stream only if necessary) does not return t when it does print a newline and nil when it does not? Here is what we said:

There are two very good reasons. The first is that the ANS says so.

The second reason is much more insightful, explaining why a more useful result cannot be returned: the value is not and can not be known at the time pprint-newline returns.

pprint-newline doesn't immediately print anything. The pretty printer works by buffering a partial line of output. pprint-newline only inserts a promise into that buffer that if whatever is printed subsequently on that line makes it necessary to break the line at that point, the newline and any subsequent indentation will occur when that line is flushed, which generally happens as soon as the conditional newline can be determined.

Why doesn't *print-level* and *print-length* affect debugging output?

This question comes up from time to time, so it is useful to remind people about it. Allegro CL has a number of print control variables which control different aspects of printing. The standard Common Lisp variables (like cl:*print-level*) only control output of Common Lisp print functions. In contrast, tpl:*print-level* and friends control what is printed when a form is evaluated at a Lisp prompt. Debugging output is controlled by other print variables. Here are all the varieties of print level (with the initial values shown):

cl:*print-level*       value: nil
excl:*trace-print-level* value: 5
excl:*step-print-level*  value: 4
tpl:*zoom-print-level* value: 3
tpl:*print-level*   value: 5

There are *print-length* equivalents of all those varibles and some also have *print-circle* equivalents. See here for information on how you can get the Allegro-specific print variables to follow the standard Common Lisp print variables.

Customize sequence function is faster than standard

A customer wrote some custom concatenate code:

(defun test-concat (min-size random-size)
  (let (arrays)
    (setf arrays (loop for i from 0 to 10000 collect
                       (apply #'vector 
                              (loop for i from 0 to 
                                    (+ min-size
                                       (random random-size)) 
                                  collect i))))
    (time (dotimes (i 100) (apply #'concatenate 'array arrays)))
    (time   (dotimes (i 100)
              (let* ((len (reduce #'+ arrays :key #'length))
                     (seq (make-array len))
                     (i 0))
                (declare (fixnum i))
                (loop for array in arrays
                    do (loop for e across array
                           do (progn
                                (assert e)
                                (setf (aref seq i) e)
                                (incf i)))))))))

Testing revealed when min-size and random-size are both 500, the custom code is a bot slower than the standard concatenate, but for small values (5 and 5), the custom code was significantly (more than 20x) faster.

This is true and the reason is the custom code knows it is concatenating vectors which concatenate has to test each argument to see whether each argumeny is a list or a vector (since concatenate takes either as arguments). That test is fast, but when there are very many short sequences, the test time starts to dominate the total time and the custom code wins out.

The moral is you can often do better than standard code in specific situations, because you can use information which is not available to (or hard to communicate to) standard functionality. However, the difference is often not worth the cost of writing the custom code.

Copyright © 2019 Franz Inc., All Rights Reserved | Privacy Statement Twitter Google+