CL-USER
CL-USER

Reputation: 786

Distinguishing an integer from a string vector

I am trying to dispatch on the type of an array. Here's a test case:

(defun column-summary2 (column)
  (typecase column
    (simple-double-float-vector (format t "Column is a simple-double-float-vector~%"))
    ;; (simple-integer-vector (format t "Column is a simple-integer-vector~%"))
    ;; (simple-string-vector  (format t "Column is a simple-string-vector~%"))
    ((simple-array string (*)) (format t "~A Column is a string-array~%" column))
    ((simple-array float (*)) (format t "~A is a simple-float-array~%" column))
    ((simple-array integer (*)) (format t "~A is a simple-float-array~%" column))
    (bit-vector (make-bit-vector-summary :length (length column) :count (count 1 column))))))

This works as expected for the built in type, bit-vector, and with my own simple-double-float-vector type:

(deftype simple-double-float-vector (&optional (length '*))
  "Simple vector of double-float elements."
  `(simple-array double-float (,length)))

but fails for string and integer:

LS-USER> (df::column-summary2 #("foo" "bar" "baz"))
#(foo bar baz) Column is a string-array
NIL
LS-USER> (df::column-summary2 #(1 2 3))
#(1 2 3) Column is a string-array

I tried defining types for these two:

(deftype simple-integer-vector (&optional (length '*))
  "Simple vector of integer elements."
  `(simple-array integer (,length)))

(deftype simple-string-vector (&optional (length '*))
  "Simple vector of integer elements."
  `(simple-array string (,length)))

Edit: Coerce also seems to fail:

CL-USER> (type-of (coerce #(4 4 1 1 2 1 4 2 2 4 4 3 3 3 4 4 4 1 2 1 1 2 2 4 2 1 2 2 4 6 8 2) '(simple-array integer (32))))
(SIMPLE-VECTOR 32)
CL-USER> (type-of (coerce #("foo" "bar" "baz") '(simple-array string (3))))
(SIMPLE-VECTOR 3)

but it doesn't help. It seems that integer and string are always conflated. Can anyone see why?

Upvotes: 3

Views: 90

Answers (2)

ignis volens
ignis volens

Reputation: 9282

typecase can only distinguish types that are implementationally distinct, and it is very unlikely that arrays of integers and strings are. You can test this by, for instance:

(eq (upgraded-array-element-type 'integer)
    (upgraded-array-element-type 'string))

Which will very likely return t. And in fact it's likely that upgraded-array-element-type on both these types is itself t: the most specialised array that can store a general string is the same as the one that can store a general integer, since both of these types really require the elements of the array to be general pointers.

The thing here is that when typecase sees an array all it can dispatch on is the implementational type of the array, rather than anything else and those two types are the same in many cases where they are not the same conceptually.

Upvotes: 4

Svante
Svante

Reputation: 51521

An array's type can only be the type given to make-array as its :element-type, see type simple-array in the spec. If you use array literals, that is likely not the case.

It does not check at runtime the type of each element.

The word can is a hint that this is also influenced by the upgrading of array element types: there is only a fixed set (implementation defined) of array types, mostly determined by whether there is a specialized representation. The actual array element type is the most specialized of that set that fits the declared type.

If you need the exact information at runtime, you need to wrap and tag yourself.

Upvotes: 2

Related Questions