writing out expressions prepended by #.

367 views
Skip to first unread message

dco...@genworks.com

unread,
Apr 27, 2001, 6:12:19 PM4/27/01
to

Sorry if i'm missing something obvious, but:...

What is the proper way to write out expressions which
are prepended with "#.", so they will be evaluated
upon being read?

For example, I have a list of "point" objects, which
have to be created with the function (macro, actually)
"make-point" (that is, their printed representation is
not sufficient to read them back in, so one has to
re-create them by evaluating an expression). So I want
to write this list out as a list of expressions, similar
to this:

(#.(make-point 1 2 3)
#.(make-point 4 5 6)
...)

such that when read in, each expression will automatically
be converted into the result of the call to make-point.

I can make a list of the make-point expressions with
something like the following:

(mapcar #'point-expression point-list)

which will produce

((make-point 1 2 3)
(make-point 4 5 6)
...)

But, how do I produce a list in which each element ends
up prepended by the "#." in the printed representation?

(I know i could kludge something together with strings
containing the "#.", but I am hoping to be able to
pretty-print an actual list)

Thanks!

-dave

Barry Margolin

unread,
Apr 27, 2001, 6:40:40 PM4/27/01
to

There's nothing that automatically prints objects using #. syntax. If
you've defined the POINT type using DEFCLASS or DEFSTRUCT, you can arrange
for a user-defined function to be used to print them:

(defmethod print-object (self stream)
(format stream (if (or *print-readably* *print-escape*)
"#.(make-point ~S ~S ~S)"
"#.(make-point ~A ~A ~A)")
(point-x self) (point-y self) (point-z self)))

--
Barry Margolin, bar...@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

Tim Moore

unread,
Apr 27, 2001, 6:59:25 PM4/27/01
to
It sounds like you're looking for some magic value that gets printed out
as "#.". There's no portable way to do that, though some implementations
might have some magic that enables that. To do what you want I'd try
this:

(defmethod print-object ((object point) stream)
(if *print-readably*
(format stream "#.(make-point ~S ~S ~S)"
(x object) (y object) (z object))
(print-unreadable-object (object stream :type t :identity t))))

If I were going to go nuts with pretty printing I'd do this:

(defmethod print-object ((object point) stream)
(if *print-readably*
(format stream "#.~:<make-point~@_ ~S~_~S~_~S~:>"
(x object) (y object) (z object))
(print-unreadable-object (object stream :type t :identity t))))

Of course, if I were going to do this I'd test any code I read on Usenet,
assuming the author hadn't :)

Tim

Kent M Pitman

unread,
Apr 27, 2001, 7:42:50 PM4/27/01
to
Barry Margolin <bar...@genuity.net> writes:

> There's nothing that automatically prints objects using #. syntax. If
> you've defined the POINT type using DEFCLASS or DEFSTRUCT, you can arrange
> for a user-defined function to be used to print them:
>
> (defmethod print-object (self stream)
> (format stream (if (or *print-readably* *print-escape*)
> "#.(make-point ~S ~S ~S)"
> "#.(make-point ~A ~A ~A)")
> (point-x self) (point-y self) (point-z self)))

Careful to specify the class, as in:

(defmethod print-object ((self point) stream)
...)


Doing it without the class defaults to class T, which is something users
shouldn't be redefining.

I assume this was just a typo on Barry's part, but wanted to make sure
anyone experimenting does this right.

- - - - -

Of course, the other way to do this is to define the point with defstruct
and then it will be re-readable with #S with no special work.

The only disadvantage of #.(...) is that a lot of people bind
*read-eval* when reading forms, and in that case you'll lose on the
re-read. To counteract that, you might be better off to take some
other dispatch on # and make something similar to #S but with
user-definable characteristics. e.g., define a #_ or #U or some other
undefined thing to read a (POINT :X 3 :Y 4 :Z 5) and turn it into
(apply #'make-instance the-expression-seen-by-read) to obtain the
object to return. It's a darned shame that #S is not thusly defined.
I seem to recall I tried to get the committee to generalize its effect
but for some reason it didn't go through--maybe it was after the feature
freeze...

Erik Naggum

unread,
Apr 27, 2001, 10:34:52 PM4/27/01
to
* Kent M Pitman

> The only disadvantage of #.(...) is that a lot of people bind *read-eval*
> when reading forms, and in that case you'll lose on the re-read. To
> counteract that, you might be better off to take some other dispatch on #
> and make something similar to #S but with user-definable characteristics.
:

> I seem to recall I tried to get the committee to generalize its effect
> but for some reason it didn't go through--maybe it was after the feature
> freeze...

I have taken to define #/ as a reader-macro akin to the format control,
to read _registered_ types with associated functions, as I ran out of
convenient ways to specify classes and such.

In this particular case, #/point/(1 2 3), would require a registered type
named point with an associated reader function that that would read as
many arguments as it needs using whatever printer controls and reader
functions it pleased, but it would of course be the question of more or
less reasoanble usages, as with every other powerful mechanism. E.g.,
#/ip/192.168.150.155/26 could mean the address and a 26-bit mask for
routing or matching purposes, whild #/ip/192.168.150.155:5555 could mean
an address and a port number. And #/md5/0123456789abcdeffedcba9876543210
could mean the hexadecimal representation of an MD5 sum.

Given this framework, would be a very simple task to supply macros that
defined and registered reader functions and corresponding print-object
methods for new classes.

#:Erik
--
I found no peace in solitude.
I found no chaos in catastrophe.
-- :wumpscut:

David Bakhash

unread,
Apr 28, 2001, 1:43:00 AM4/28/01
to
>>>>> "kmp" == Kent M Pitman <Kent> writes:

kmp> It's a darned shame that #S is not thusly defined.

never thought about that, but yeah, since it's not asking to much to
make an implementation allow make-instance to work with structs as
well (I know most commercial ones do), this is an obvious extension.

So here's a question I probably should have asked ages ago:

What would it take to have this change occur in ANSI Common Lisp?
i.e. when will Common Lisp change, if ever?

I wouldn't mind things like this changing, though I also do realize
that such a change would modify the behaviour of some of the code
that's out there. But in a case like this, it seems worthwhile.

dave

Kent M Pitman

unread,
Apr 28, 2001, 9:15:28 AM4/28/01
to
Erik Naggum <er...@naggum.net> writes:

> * Kent M Pitman
> > The only disadvantage of #.(...) is that a lot of people bind *read-eval*
> > when reading forms, and in that case you'll lose on the re-read. To
> > counteract that, you might be better off to take some other dispatch on #
> > and make something similar to #S but with user-definable characteristics.
> :
> > I seem to recall I tried to get the committee to generalize its effect
> > but for some reason it didn't go through--maybe it was after the feature
> > freeze...
>
> I have taken to define #/ as a reader-macro akin to the format control,
> to read _registered_ types with associated functions, as I ran out of
> convenient ways to specify classes and such.
>
> In this particular case, #/point/(1 2 3), would require a registered type
> named point with an associated reader function that that would read as
> many arguments as it needs using whatever printer controls and reader
> functions it pleased, but it would of course be the question of more or
> less reasoanble usages, as with every other powerful mechanism. E.g.,
> #/ip/192.168.150.155/26 could mean the address and a 26-bit mask for
> routing or matching purposes, whild #/ip/192.168.150.155:5555 could mean
> an address and a port number. And #/md5/0123456789abcdeffedcba9876543210
> could mean the hexadecimal representation of an MD5 sum.

The only problem with this is that there is a convention (not sure if it's
documented or not) that #<char> should be followed by exactly one readable
token, for the case of making #- skip the right number of tokesn in a place
where the object is not implemented.

> Given this framework, would be a very simple task to supply macros that
> defined and registered reader functions and corresponding print-object
> methods for new classes.

I agree with your intent, and another notation I've periodically wished for
is like this: #_point(1 2 3)

But because of other practicalities would rather
#S(MD5 :CODE "0123456789abcdeffedcba9876543210")
and #S(IP :ADDRESS "192.168.150.155" :PORT "5555").
Absent support for extensible #S, #_ or whatever.
The other advantage of the #S approach is that if the parser is proprietary
for things like an IP address or a URL, it's easier to conjure a "parser" for
#S-style structures (since it's just an application of GETF) than for the
other notations you suggest, even if they are more compact.

Kent M Pitman

unread,
Apr 28, 2001, 9:31:10 AM4/28/01
to
David Bakhash <ca...@alum.mit.edu> writes:

> >>>>> "kmp" == Kent M Pitman <Kent> writes:
>
> kmp> It's a darned shame that #S is not thusly defined.
>
> never thought about that, but yeah, since it's not asking to much to
> make an implementation allow make-instance to work with structs as
> well (I know most commercial ones do), this is an obvious extension.
>
> So here's a question I probably should have asked ages ago:
>
> What would it take to have this change occur in ANSI Common Lisp?

A miracle.

> i.e. when will Common Lisp change, if ever?

Hopefully never. The cost of it changing is very high.

> I wouldn't mind things like this changing, though I also do realize
> that such a change would modify the behaviour of some of the code
> that's out there. But in a case like this, it seems worthwhile.

I think the change could be made enough compatibly that individual vendors
might be convinced to make this change as an extension, and we might be
able to have it be a de facto standard.

Send a bug report to your vendors. Let's see if we can rally enough
individual vendors to make the extension that it gets in.

I *think* this is what I would ask for. Someone correct me if this doesn't
sound right:

If the <NAME> in #S(<NAME> . <OPTIONS>) is not defined as a structure,
the effect of (APPLY #'MAKE-INSTANCE '<NAME> '<OPTIONS>)
is done.

Frankly, I've never understood why we didn't just make it so that

(MAKE-INSTANCE '<structure-class-name> ...)

was a synonym for

(MAKE-<structure-class-name> ...)

[well, or vice versa]

Then it would be really obvious how to implement #S and the extension
would fall out naturally instead of requiring dealing with structures
and non-structures as special cases.

One reason I advocated this #S notation, btw, is that it can be used for
built-in-classes as well, especially when *PRINT-READABLY* is true and
the standard syntax isn't enough to allow proper re-reading of the
structure. For example, hash tables, displaced arrays, etc.
It requires some detailing to make sure the names of the "slots"
of these objects, such as they are, are named. e.g., to decide that
an array's contents is its :contents or :storage or whatever, and whether
the length is explicit or implicit, and whether the contents beyond the
fill pointer is shown, and all that. But still, these are things that
could have been worked out, and would have been useful to the community.

#S(VECTOR :CONTENTS (1 2) :LENGTH 5 :FILL-POINTER 2 :ADJUSTABLE T)
vs
#S(VECTOR :CONTENTS (1 2 3 4 5) :LENGTH 5 :FILL-POINTER 2 :ADJUSTABLE T)

or

(SETQ X #1=#(1 2 3 4 5 6)
Y #S(ARRAY :DISPLACED-TO #1# :DISPLACED-INDEX-OFFSET 2
:DIMENSIONS (2 2)))


david....@genworks.com

unread,
Apr 28, 2001, 8:44:20 PM4/28/01
to

Dear Barry, Tim, and Kent,

Thank you for your replies. The defmethod of print-object is
what the doctor ordered. I couldn't define a new print-object
method for this point type itself, as doing so would have
affected the printing of points everywhere else in the
application (e.g. in object inspectors etc.)

So what I ended up doing was (the point type I am dealing
with supports accessors get-x, get-y, and get-z):

(defstruct icad-3d-point x y z)

(defun magic-point-expression (point)
(make-icad-3d-point :x (get-x point)
:y (get-y point)
:z (get-z point)))

(cl:defmethod print-object ((point icad-3d-point) stream)
(format stream "#.(make-point ~a ~a ~a)"
(icad-3d-point-x point)
(icad-3d-point-y point)
(icad-3d-point-z point)))

(I had to specify the "cl" package with defmethod because
by default any package using the "icad-supported" package
sees the Flavors defmethod rather than the CLOS one).

Now I can write out a point-list readably with:

(pprint (mapcar #'magic-point-expression point-list))

or simply

(print (mapcar #'magic-point-expression point-list))

There is of course some overhead involved in creating
all those temporary structs when doing the output, but
for my usage this will not be an issue.

Thanks again,

-dave

J. Cooper <dcooper" @genworks.com >=3AEB63E4.D26E2F6C%40genworks.com&number=2508 »b

unread,
Apr 28, 2001, 8:53:58 PM4/28/01
to

Erik Naggum

unread,
Apr 29, 2001, 8:54:15 PM4/29/01
to
* Kent M Pitman

> The only problem with this is that there is a convention (not sure if
> it's documented or not) that #<char> should be followed by exactly one
> readable token, for the case of making #- skip the right number of tokesn
> in a place where the object is not implemented.

I do not agree with this. Input to the reader that use these notations
either have an in-syntax form at the top to establish the readtable, and
that would fail before #- would fail, or operate under an assumption made
explicit elsewhere if not in the input stream that such readtables are
employed.

> The other advantage of the #S approach is that if the parser is
> proprietary for things like an IP address or a URL, it's easier to
> conjure a "parser" for #S-style structures (since it's just an
> application of GETF) than for the other notations you suggest, even if
> they are more compact.

Compactness is not the driving concern. The driving concern is the
ability to register types (or whatever else is suitable) so that the
syntax is _not_ open-ended as far as the reading process is concerned,
such as #. is. #. also returns objects of type t, whatever is returned
as the primary value of the form evaluated, which may be a strain on the
human reader of the code. #/.../ would return objects of a known type,
identified by name. #S has this feature, but it requires a parsed-out
list of arguments. There are good reasons we invent new syntaxes, such
as for pathnames, symbols-with-packages, etc, even though they have some
costs: e.g., #S(symbol :name "FOO" :package "BAR") is conceptually the
same as bar:foo.

Steve Long

unread,
May 4, 2001, 1:28:49 AM5/4/01
to
I really wish ICAD didn't clobbered defmethod either. My personal
preference is to use pure Lisp
(especially CLOS) over IDL whenever possible. We deal with enormous
amounts of data and the difference between
loop and let-streams is significant.

Steve Long

Reply all
Reply to author
Forward
0 new messages