SlightlyCyborg
SlightlyCyborg

Reputation: 23

Why is this lisp vector not extending?

I am trying to make a node object in Common Lisp using SBCL that is initialized with its text element and then has links to other nodes. My function link is supposed to take node "from_node", get its member links (which should be a mutable/extendable vector) and push in the node "to_node".

I compile say.lisp, create 2 global variables that represent nodes and then try to link the two nodes. I am getting an error

Here is say.lisp

(defclass node ()
  ((text
     :initarg :text)
   (links
     :initform (make-array 1 :adjustable t))))

(defun link (from_node to_node)
  (vector-push-extend to_node (slot-value from_node 'links)))

And then in the REPL

* (load "say.lisp")  
T
* (defvar *x* (make-instance 'node :text "hello world"))

*X*
* (defvar *y* (make-instance 'node :text "bye world"))  

*Y*
* (link *x* *y*)

debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {1003016593}>:
  The value #() is not of type (AND VECTOR (NOT SIMPLE-ARRAY)).

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(VECTOR-PUSH-EXTEND #<NODE {10031D3983}> #() NIL)
0] 

Originally I thought that I was making an immutable vector, but ":adjustable t" should allow this to work.

What is wrong?

Upvotes: 2

Views: 912

Answers (1)

Joshua Taylor
Joshua Taylor

Reputation: 85833

VECTOR-PUSH-EXTEND requires that the vector argument is a "vector with a fill pointer." Passing :adjustable t to the make-array makes it adjustable, but doesn't give it a fill pointer. For instance, without a fill pointer:

CL-USER> (defparameter *x* (make-array 1 :adjustable t))
*X*
CL-USER> *x*
#(0)
CL-USER> (vector-push-extend 3 *x*)
; Evaluation aborted on #<SIMPLE-TYPE-ERROR expected-type:
                    (AND VECTOR (SATISFIES ARRAY-HAS-FILL-POINTER-P))
                    datum: #<(VECTOR T 1) {100464C57F}>>.

With a fill pointer:

CL-USER> (defparameter *x* (make-array 1 :adjustable t :fill-pointer 0))
*X*
CL-USER> *x*
#()
CL-USER> (vector-push-extend 3 *x*)
0
CL-USER> (vector-push-extend 4 *x*)
1
CL-USER> (vector-push-extend 5 *x*)
2
CL-USER> *x*
#(3 4 5)

It's an important difference because you can have adjustable arrays without fill pointers, as you've seen. These can be resized, but always appear to have as many elements as there is space. (E.g., in the first case, *x* has length one. You can also have arrays will fill pointers that are not adjustable. These would still allow you to use vector-push and vector-push-extend until they were filled up, but couldn't be resized afterward.

Upvotes: 5

Related Questions