From: Adam Warner
Subject: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <pan.2003.12.13.11.30.11.852610@consulting.net.nz>
Depending upon whether an implementation supports specialised arrays
and optimised array lookup:

(defmacro myref (a i)
  `(aref (the (simple-array double-float) ,a) ,i))

(defparameter *a*
  (make-array 10 :element-type 'double-float :initial-element 0d0))

(myref *a* 4) => 0.0d0

(defparameter *a* (read-from-string (format nil "~S" *a*)))

*a* => #(0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0)

(myref *a* 4)
=> debugger invoked on a TYPE-ERROR in thread 2777:
   The value #(0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 ...)
   is not of type
    (SIMPLE-ARRAY DOUBLE-FLOAT).

(type-of *a*) => (simple-vector 10)

Moral: You can still use READ but you must manually set each element of
the original array to the corresponding element of the read array.

Regards,
Adam

From: Rob Warnock
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <6KicnaTGGdapZUeiXTWc-w@speakeasy.net>
Adam Warner  <······@consulting.net.nz> wrote:
+---------------
| (defparameter *a* (read-from-string (format nil "~S" *a*)))
| *a* => #(0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0)
...
| (type-of *a*) => (simple-vector 10)
| 
| Moral: You can still use READ but you must manually set each element of
| the original array to the corresponding element of the read array.
+---------------

Well, what about this, then?

	> (make-array 10 :element-type 'double-float
	    :initial-contents '(0d0 1d0 2d0 3d0 4d0 5d0 6d0 7d0 8d0 9d0))

	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
	> (type-of *) 

	(SIMPLE-ARRAY DOUBLE-FLOAT (10))
	> 


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Adam Warner
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <pan.2003.12.13.12.33.55.713353@consulting.net.nz>
Hi Rob Warnock,

> Adam Warner  <······@consulting.net.nz> wrote:
> +---------------
> | (defparameter *a* (read-from-string (format nil "~S" *a*)))
> | *a* => #(0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0 0.0d0)
> ...
> | (type-of *a*) => (simple-vector 10)
> | 
> | Moral: You can still use READ but you must manually set each element of
> | the original array to the corresponding element of the read array.
> +---------------
> 
> Well, what about this, then?
> 
> 	> (make-array 10 :element-type 'double-float
> 	    :initial-contents '(0d0 1d0 2d0 3d0 4d0 5d0 6d0 7d0 8d0 9d0))
> 
> 	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
> 	> (type-of *) 
> 
> 	(SIMPLE-ARRAY DOUBLE-FLOAT (10))
> 	> 

What's your point Rob? (Mine was that the semantics of readable printed
objects broke because Common Lisp doesn't print and read back in the type
of the array along with the array contents)

As the array lookups were coded for arrays of (simple-array double-float)
it should be clear that I know I was operating on arrays of (simple-array
double-float). Yet when the arrays of (simple-array double-float) were
printed out and read back in they became simple-arrays able to hold any
type (which probably also have a different storage representation). The
program's assembly code which is specialised for double-float array lookup
cannot operate upon the new array.

Fixing this is fertile ground for a language extension. I can't see how
the printed representation could be backwards compatible unless the type
information was encoded in a comment. But that would be a horrible kludge.

Are there any other simple Common Lisp objects which change to a different
type after their readable and printed representation is read back in?

Regards,
Adam
From: james anderson
Subject: Re: Interesting type issue (a.k.a. breaking your programs using  specialised arrays and print/read)
Date: 
Message-ID: <3FDB24E9.2C1004B3@setf.de>
Adam Warner wrote:
> 
> 
> 
> Are there any other simple Common Lisp objects which change to a different
> type after their readable and printed representation is read back in?
> 

if one intends to code portably, the robust approach is to presume the storage
form for only those objects which one has allocated explicity. if one didn't
write the reader macro, one should not presume. otherwise one is on
non-conformant ice.

- some implementations do not always characterise the floating point
representation when printing the respective default representation. when such
expressions are read by another implementation with a different default, the
representation will differ.

- specialized arrays, as reported. array representation and element
representation may be distinct issues. adjustability and fill are additional
ephemeral properties.

- character widths. probably an issue in relation to specialized arrays only.

- structure representation, though not in the sense one is concerned about here.

? it is not clear that a conformant application should be able to distinguish
them anyway. that is, is the noted error, which arises by operating on data
which id not of the declared type, a conformance blindspot in the application,
the implementation, or the spec?
From: Adam Warner
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <pan.2003.12.14.00.30.53.163053@consulting.net.nz>
Hi james anderson,

> ? it is not clear that a conformant application should be able to
> distinguish them anyway. that is, is the noted error, which arises by
> operating on data which is not of the declared type, a conformance
> blindspot in the application, the implementation, or the spec?

It's a limitation of the specification that some type-specific arrays
cannot be portably read back in (it appears only type t and bit-vectors
can be). "22.1.3.7 Printing Other Vectors" states that "If *print-readably*
is true, the vector prints in an implementation-defined manner; see the
variable *print-readably*."

When *PRINT-READABLY* is true, CMUCL preserves the type data!
(setf *print-readably* t)
(defparameter *a* (make-array 5 :element-type 'double-float :initial-element 0d0))
*a* => #A(double-float (5) (0.0d0 0.0d0 0.0d0 0.0d0 0.0d0)
(setf *a* (read-from-string (format nil "~S" *a*)))
(type-of *a*) => (simple-array double-float (5))

Unfortunately the functionality appears to have been removed from SBCL:
(setf *print-readably* t)
(defparameter *a* (make-array 5 :element-type 'double-float :initial-element 0d0))
*a* =>
debugger invoked on a PRINT-NOT-READABLE in thread 1474:
  #<(SIMPLE-ARRAY DOUBLE-FLOAT (5)) {912006F}> cannot be printed readably.

You can type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT   ] Reduce debugger level (leaving debugger, returning to toplevel).
  1: [TOPLEVEL] Restart at toplevel READ/EVAL/PRINT loop.
(SB-PRETTY::PPRINT-ARRAY
 2
 #<SB-PRETTY:PRETTY-STREAM {9123739}>
 #(0.0d0 0.0d0 0.0d0 0.0d0 0.0d0))[:EXTERNAL]
0] 

So long as an implementation supports the printing of type-specific arrays
when *PRINT-READABLY* is true a program that prints and reads type-specific
arrays should work (even when transferred to another implementation that
doesn't support type-specific array data types; and so long as the
implementation's print format is being read). But a program that does this
won't be "portable" because it depends upon implementation-defined behaviour
(the mere ability to print and read a type-specific array).

Regards,
Adam
From: Adam Warner
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <pan.2003.12.14.00.55.56.722980@consulting.net.nz>
> It's a limitation of the specification that some type-specific arrays
> cannot be portably read back in (it appears only type t and bit-vectors
> can be).

And perhaps strings, which are a specific type of vector. ANSI Common Lisp
has a standard character repertoire but I have not found any character
encoding mandated for portability. If no character set like ASCII is
mandated then at its core no printed representation is portable. The
implementations just all happen to support ASCII by default (as a subset
of Latin-1 or UTF-8).

I'm reminded of Kent M Pitman's insightful comment about languages and
data types:
<http://groups.google.com/groups?selm=sfwr82zxa46.fsf%40shell01.TheWorld.com>

Regards,
Adam
From: Rob Warnock
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <8vecnWotBoKx3UGiXTWc-w@speakeasy.net>
Adam Warner  <······@consulting.net.nz> wrote:
+---------------
| Hi Rob Warnock,
| > Well, what about this, then?
| > 
| > 	> (make-array 10 :element-type 'double-float
| > 	    :initial-contents '(0d0 1d0 2d0 3d0 4d0 5d0 6d0 7d0 8d0 9d0))
| > 
| > 	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
| > 	> (type-of *) 
| > 
| > 	(SIMPLE-ARRAY DOUBLE-FLOAT (10))
| > 	> 
| 
| What's your point Rob? (Mine was that the semantics of readable printed
| objects broke because Common Lisp doesn't print and read back in the type
| of the array along with the array contents)
+---------------

And mine was only that there is a simple workaround for the problem you
presented that is less painful than looping over a setf/aref, namely,
passing the vector you got from READ to MAKE-ARRAY's :INITIAL-CONTENTS.

Though I apologize for not making it clear in my example that
:INITIAL-CONTENTS can take a sequence, not just a list, and that
the :ELEMENT-TYPE could be picked out of the read-in array, but
you already knew all that, yes?

	> (read-from-string "#(0d0 1d0 2d0 3d0 4d0 5d0 6d0 7d0 8d0 9d0)")
	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
	> (type-of *)

	(SIMPLE-VECTOR 10)
	> (make-array 10 :element-type (type-of (aref ** 0))
			 :initial-contents **)

	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
	> (type-of *)

	(SIMPLE-ARRAY DOUBLE-FLOAT (10))
	> 


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Rob Warnock
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <ivacnadYGYRN1kGiXTWc-w@speakeasy.net>
Adam Warner  <······@consulting.net.nz> wrote:
+---------------
| Hi Rob Warnock,
| > Well, what about this, then?
| > 	> (make-array 10 :element-type 'double-float
| > 	    :initial-contents '(0d0 1d0 2d0 3d0 4d0 5d0 6d0 7d0 8d0 9d0))
| > 	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
| > 	> (type-of *) 
| > 	(SIMPLE-ARRAY DOUBLE-FLOAT (10))
| 
| What's your point Rob? (Mine was that the semantics of readable printed
| objects broke because Common Lisp doesn't print and read back in the type
| of the array along with the array contents)
+---------------

And mine was simply that there is an obvious workaround for the problem
you presented [reading specialized vectors] that is less painful than
looping over a setf/aref, namely, passing the vector you got from READ
to MAKE-ARRAY's :INITIAL-CONTENTS.

Though I apologize for not making it clear in my example that it can be
made fully automatic, provided that you *know* the array you've just read
is homogenous:

- The dimensions can be extracted from the read-in array;
- The :ELEMENT-TYPE can be extracted from the read-in array;
- :INITIAL-CONTENTS can take a vector, not just a list.

But you already knew all that, yes?

	> (defun coerce-to-specialized-array (x)
	    (make-array (array-dimensions x)
		        :element-type (type-of (row-major-aref x 0))
		        :initial-contents x))

	COERCE-TO-SPECIALIZED-ARRAY
	> (read-from-string "#(0d0 1d0 2d0 3d0 4d0 5d0 6d0 7d0 8d0 9d0)")

	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
	42
	> (type-of *)

	(SIMPLE-VECTOR 10)
	> (coerce-to-specialized-array **)

	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
	> (type-of *)

	(SIMPLE-ARRAY DOUBLE-FLOAT (10))
	> 

Note: The above COERCE-TO-SPECIALIZED-ARRAY has a problem for multi-
dimensional arrays, since the latter are not "sequences" [vectors &
lists only], and hence are not valid arguments for :INITIAL-CONTENTS.
Is there a standard idiom to fix this?


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Christophe Rhodes
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <sqekv7uto0.fsf@lambda.jcn.srcf.net>
····@rpw3.org (Rob Warnock) writes:

> Note: The above COERCE-TO-SPECIALIZED-ARRAY has a problem for multi-
> dimensional arrays, since the latter are not "sequences" [vectors &
> lists only], and hence are not valid arguments for :INITIAL-CONTENTS.
> Is there a standard idiom to fix this?

Something in my brain says that there's a trick with ADJUST-ARRAY and
displaced arrays to do this.  I think I'm thinking of a posting on
lemonodor (John Wiseman's weblog, linked from CLiki).

A little digging reveals <http://lemonodor.com/archives/000100.html>.

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Marco Antoniotti
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <DEoDb.29$Nq.26875@typhoon.nyu.edu>
Christophe Rhodes wrote:

> ····@rpw3.org (Rob Warnock) writes:
> 
> 
>>Note: The above COERCE-TO-SPECIALIZED-ARRAY has a problem for multi-
>>dimensional arrays, since the latter are not "sequences" [vectors &
>>lists only], and hence are not valid arguments for :INITIAL-CONTENTS.
>>Is there a standard idiom to fix this?
> 
> 
> Something in my brain says that there's a trick with ADJUST-ARRAY and
> displaced arrays to do this.  I think I'm thinking of a posting on
> lemonodor (John Wiseman's weblog, linked from CLiki).
> 
> A little digging reveals <http://lemonodor.com/archives/000100.html>.


Very cute trick.  And you just "waste" two array headers for that.

Cheers
--
Marco
From: Adam Warner
Subject: Re: Interesting type issue (a.k.a. breaking your programs using specialised arrays and print/read)
Date: 
Message-ID: <pan.2003.12.14.12.54.10.532179@consulting.net.nz>
Hi Rob Warnock,

> +---------------
> | Hi Rob Warnock,
> | > Well, what about this, then?
> | > 	> (make-array 10 :element-type 'double-float
> | > 	    :initial-contents '(0d0 1d0 2d0 3d0 4d0 5d0 6d0 7d0 8d0 9d0))
> | > 	#(0.0d0 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0)
> | > 	> (type-of *) 
> | > 	(SIMPLE-ARRAY DOUBLE-FLOAT (10))
> | 
> | What's your point Rob? (Mine was that the semantics of readable printed
> | objects broke because Common Lisp doesn't print and read back in the type
> | of the array along with the array contents)
> +---------------
> 
> And mine was simply that there is an obvious workaround for the problem
> you presented [reading specialized vectors] that is less painful than
> looping over a setf/aref, namely, passing the vector you got from READ
> to MAKE-ARRAY's :INITIAL-CONTENTS.
> 
> Though I apologize for not making it clear in my example that it can be
> made fully automatic, provided that you *know* the array you've just read
> is homogenous:
> 
> - The dimensions can be extracted from the read-in array;
> - The :ELEMENT-TYPE can be extracted from the read-in array;
> - :INITIAL-CONTENTS can take a vector, not just a list.
> 
> But you already knew all that, yes?

Thanks! No I did not know that ":INITIAL-CONTENTS can take a vector, not
just a list." I had examined your example and noticed that you supplied
a list to initial contents so I dismissed the approach as inefficient.
Thus I was close to grasping your point and would have got it if you
supplied a vector to initial-contents, even without any written
explanation.

Thanks for the heads up about not being able to use this technique for
multi-dimensional arrays (as they are not sequences).

Regards,
Adam