Search code examples
common-lisp

How to type DO variables in common lisp?


I know you can declare function parameter types like

(defun add-integer (a b)
  (declare (integer a b))
  (the integer (+ a b)))

But what about DO variables? For example, I want to type passes:

(defun bench ()
  (do ((end (+ (get-internal-real-time) (* 5 internal-time-units-per-second)))
       (passes 0 (+ 1 passes)))
      ((> (get-internal-real-time) end)
       passes)
    (sieve 1000000)))

When I try to compile with (declaim (optimize (speed 2) (safety 0))), I get

; in: DEFUN BENCH
;     (1+ PASSES)
; 
; note: forced to do full call
;       unable to do inline fixnum arithmetic (cost 2) because:
;       The first argument is a UNSIGNED-BYTE, not a FIXNUM.
;       The result is a (VALUES (INTEGER 1) &OPTIONAL), not a (VALUES FIXNUM
;                                                                     &REST T).
;       unable to do inline (unsigned-byte 64) arithmetic (cost 5) because:
;       The first argument is a UNSIGNED-BYTE, not a (UNSIGNED-BYTE 64).
;       The result is a (VALUES (INTEGER 1) &OPTIONAL), not a (VALUES
;                                                              (UNSIGNED-BYTE 64)
;                                                              &REST T).

I tried

(defun bench ()
  (declare (type (unsigned-byte 64) passes))
  (do ((end (+ (get-internal-real-time) (* 5 internal-time-units-per-second)))
       (passes 0 (+ 1 passes)))
      ((> (get-internal-real-time) end)
       passes)
    (sieve 1000000)))

But then I get

;   Undefined variable:
;     PASSES

I cannot find anything on this in the type chapters of the HyperSpec, e.g. (http://clhs.lisp.se/Body/04_bc.htm). A working example would be extremely helpful! Thanks!


Solution

  • You put the declaration at the beginning of the loop body.

    (defun bench ()
      (do ((end (+ (get-internal-real-time) (* 5 internal-time-units-per-second)))
           (passes 0 (+ 1 passes)))
          ((> (get-internal-real-time) end)
           passes)
        (declare (type (unsigned-byte 64) passes))
        (sieve 1000000)))
    

    This is shown in the specification of DO:

    do ({var | (var [init-form [step-form]])}*) (end-test-form result-form*) declaration* {tag | statement}*

    See declaration after (end-test-form result-form*)