Pergunta

At least some implementations of Common Lisp don't allow user-defined constants to be used as array dimensions in some type specifiers. For example, in SBCL, this code:

(defconstant +len+ 3)

(defun foo (x) 
  (declare (type (simple-array fixnum (+len+)) x))
  x)

generates this error:

; in: DEFUN FOO
;     (TYPE (SIMPLE-ARRAY FIXNUM (+LEN+)) X)
; 
; caught ERROR:
;   bad dimension in array type: +LEN+

Why? It seems surprising that user-defined constants can't be used in type specifiers, since it would be desirable to be able to coordinate multiple type specifiers using some kind of global definition. I understand that type specifiers need to be completely understandable at compile-time. But I would have thought that a compiler would be able to replace symbols defined with defconstant by their literal values. I would have thought that this was one of the purposes of defconstant. (I've been unsuccessful, so far, in getting deeper understanding of this issue from the Common Lisp Hyperspec, CLTL2, the SBCL manual, or what Google has turned up. I suspect the answer is there in some form ....)

Foi útil?

Solução

I imagine that something like this would work:

(defconstant +len+ 3)

(deftype len-3-fixnum-array () `(array fixnum (,+len+)))

(defun foo (x)
  (declare (type len-3-fixnum-array x))
  (print x))

(foo (make-array 3 :element-type 'fixnum))
;; #(0 0 0)

(foo (make-array 4 :element-type 'fixnum))
;; The value #(0 0 0 0) is not of type (VECTOR FIXNUM 3).
;;    [Condition of type TYPE-ERROR]

However, you need to keep in mind that type annotations are only recommendations, compilers are allowed to ignore them altogether.


I.e. the problem isn't that you can't use constants in the declare form, it's that it acts as if it was quoted, so your constant there doesn't evaluate to 3, it just stays the symbol +len+.

Outras dicas

I had the same problem with a 2D array:

(defconstant board-width  4)
(defconstant board-height 3)

(setq *board* (make-array '(board-width board-height) :initial-element 0))

I always got the error

The value BOARD-WITH is not of type SB-INT:INDEX.

So I changed the last line this way:

(setq *board* (make-array (list board-width board-height) :initial-element 0))

and it works perfectly.

If you look at the ANSI CL spec, the syntax for types is clearly defined. For simple-array:

simple-array [{element-type | *} [dimension-spec]]

dimension-spec::= rank | * | ({dimension | *}*) 

Where:

dimension---a valid array dimension.

element-type---a type specifier.

rank---a non-negative fixnum.

The valid array dimension is then explained as a fixnum.

There are no variables or constant identifiers in an simple-array type declaration. Type declarations have their own syntax and are not evaluated or similar. They are type expressions which can appear in code in special places (-> declarations).

You can use DEFTYPE to create new type names and use those, though.

Well, because nobody seems to actually give what the questioner needs, here it is:

(defun foo (x) 
  (declare (type (simple-array fixnum (#.+len+)) x))
  x)

#. is a standard readmacro that evaluates the value in the read-time. Lisp form only sees what was expanded. http://www.lispworks.com/documentation/HyperSpec/Body/02_dhf.htm

The other two answers tell you how to work around the limitation and where the limitation is specified in the standard.

I will try to tell you where this limitation is coming from.

The reason is that the Lisp declarations are intended not just as code documentation for the human reader, but also as a help for the compiler in optimizing the code. I.e., the array size, when declared, should permit the compiler to allocate a fixed size array, if necessary, and infer the storage requirements for array indexes. It is similar to the C limitation which forbids code like

int N = 10;
int v[N];

When you start looking at the declarations from the compiler's POV, the requirements will become quite natural.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top