From: Nils Goesche
Subject: Read macro returning function?
Date: 
Message-ID: <lkvg9qeqx6.fsf@pc022.bln.elmeg.de>
Hi!

In some paper I read the author assumed the existence of a read macro
that would turn e.g. #"blark ~D" into a function that takes a stream
and an integer and prints it like FORMAT would.  One could try

(defun define-format-macro ()
  (let ((string-reader (get-macro-character #\")))
    (flet ((reader-func (stream sub param)
             (declare (ignore param))
             (let ((string (funcall string-reader stream sub)))
               (lambda (stream &rest args)
                 (apply #'format stream string args)))))
      (set-dispatch-macro-character #\# #\" #'reader-func))))

now, and this works indeed, say, in the top-level.  However, functions
are not externalizable objects, and I think it is not possible to
compile a file containing any occurrence of, say, #"blark ~D", as it
is not clear how such a closure should be dumped into a fasl file,
right?

My next attempt was

(defun define-format-macro2 ()
  (let ((string-reader (get-macro-character #\")))
    (flet ((reader-func (stream sub param)
             (declare (ignore param))
             (let ((string (funcall string-reader stream sub)))
               `(lambda (stream &rest args)
                  (apply #'format stream ,string args)))))
      (set-dispatch-macro-character #\# #\" #'reader-func))))

Is this better?  Any problems with it (besides that #\" is not
explicitly reserved for the user as a dispatch macro character)?
Should I gensym STREAMS and ARGS in the innermost expression (I think
not)?

Regards,
-- 
Nils Goesche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x42B32FC9

From: Kent M Pitman
Subject: Re: Read macro returning function?
Date: 
Message-ID: <sfwr8kecrsk.fsf@shell01.TheWorld.com>
Nils Goesche <······@cartan.de> writes:

> In some paper I read the author assumed the existence of a read macro
> that would turn e.g. #"blark ~D" into a function that takes a stream
> and an integer and prints it like FORMAT would.

I'm ignoring the rest of your post as irrelevant because you've misstated
the problem.

Readmacros should return program expressions not data (where a first class
function is data).

(defun read-sharp-doublequote (stream subchar param)
  (declare (ignore param subchar))
  (unless (eql subchar #\") (error "Subchar must be doublequote."))
  (unread-char subchar stream)
  (let ((format-string (read stream t nil t)))
    `(formatter ,format-string)))

(set-dispatch-macro-character #\# #\" 'read-sharp-doublequote)

(funcall #"foo ~X" *standard-output* 37)
foo 25
=> NIL
From: Thomas F. Burdick
Subject: Re: Read macro returning function?
Date: 
Message-ID: <xcvy9emqq28.fsf@apocalypse.OCF.Berkeley.EDU>
> Nils Goesche <······@cartan.de> writes:
> 
> > In some paper I read the author assumed the existence of a read macro
> > that would turn e.g. #"blark ~D" into a function that takes a stream
> > and an integer and prints it like FORMAT would.

I think what you mean (or meant to mean) is that #"blark ~D" names a
function that blah blah blah.  What you're trying to do is write:

  * (#"foo ~X" t 37)
  foo 25
  NIL

right?  Think of how you want that to expand.  You don't ever see code
that looks like:

  * `(,(formatter "foo ~X") t 37)
  (#<Interpreted Function (LAMBDA # # FORMAT::ARGS) {400DBB11}> T 37)

In fact, that won't work (portably, anyway).  The first place in a
form names a function.

  * (eval *)
  In:
      #<Interpreted Function (LAMBDA (STREAM &OPTIONAL # &REST FORMAT::ARGS)
                            (BLOCK NIL # #)
                            FORMAT::ARGS)
     {400DBB11}> T
    (#<Interpreted Function (LAMBDA # # FORMAT::ARGS) {400DBB11}> T 37)
  Error: Illegal function call.

What you really want is something like:

  * '((lambda (stream x) (funcall (formatter "foo ~X") stream x)) t 37)
  ((LAMBDA (STREAM X) (FUNCALL (FORMATTER "foo ~X") STREAM X)) T 37)
  * (eval *)
  foo 25
  NIL

So you want #"foo ~X" to expand into something naming an anonymous
function, like above.

Kent M Pitman <······@world.std.com> writes:

> I'm ignoring the rest of your post as irrelevant because you've misstated
> the problem.
> 
> Readmacros should return program expressions not data (where a first class
> function is data).
> 
> (defun read-sharp-doublequote (stream subchar param)
>   (declare (ignore param subchar))
>   (unless (eql subchar #\") (error "Subchar must be doublequote."))
>   (unread-char subchar stream)
>   (let ((format-string (read stream t nil t)))
>     `(formatter ,format-string)))

To change this up a bit, to make the readmacro name a function,
instead of creating a function object:

  * (defun read-sharp-doublequote (stream subchar param)
      (declare (ignore param))
      (unless (eql subchar #\") (error "Subchar must be doublequote."))
      (unread-char subchar stream)
      (let ((format-string (read stream t nil t)))
        `(lambda (&rest args) (apply (formatter ,format-string) args))))
  READ-SHARP-DOUBLEQUOTE
  * (set-dispatch-macro-character #\# #\" 'read-sharp-doublequote)
  #<Interpreted Function READ-SHARP-DOUBLEQUOTE {40084419}>
  * (#"foo ~X" t 37)
  foo 25
  NIL

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Kent M Pitman
Subject: Re: Read macro returning function?
Date: 
Message-ID: <sfw7km6b8v3.fsf@shell01.TheWorld.com>
···@apocalypse.OCF.Berkeley.EDU (Thomas F. Burdick) writes:

>   * (defun read-sharp-doublequote (stream subchar param)
>       (declare (ignore param))
>       (unless (eql subchar #\") (error "Subchar must be doublequote."))
>       (unread-char subchar stream)
>       (let ((format-string (read stream t nil t)))
>         `(lambda (&rest args) (apply (formatter ,format-string) args))))
>   READ-SHARP-DOUBLEQUOTE
>   * (set-dispatch-macro-character #\# #\" 'read-sharp-doublequote)
>   #<Interpreted Function READ-SHARP-DOUBLEQUOTE {40084419}>
>   * (#"foo ~X" t 37)
>   foo 25
>   NIL

T is not a proper value for *standard-output* so is not a proper value
for an arg to a FORMATTER-produced function.

The arguments to such functions should be "streams", not "output stream
designators".

The reason I sugggest using FORMAT is that does the coercion of the string
argument.  It also presumably manages the case of a string with fill pointer
for the stream position.
From: Joe Marshall
Subject: Re: Read macro returning function?
Date: 
Message-ID: <vfgE8.4536$Bn5.1691037@typhoon.ne.ipsvc.net>
"Kent M Pitman" <······@world.std.com> wrote in message ····················@shell01.TheWorld.com...
> Nils Goesche <······@cartan.de> writes:
>
> > In some paper I read the author assumed the existence of a read macro
> > that would turn e.g. #"blark ~D" into a function that takes a stream
> > and an integer and prints it like FORMAT would.
>
> I'm ignoring the rest of your post as irrelevant because you've misstated
> the problem.
>
> Readmacros should return program expressions not data (where a first class
> function is data).
>
> (defun read-sharp-doublequote (stream subchar param)
>   (declare (ignore param subchar))
>   (unless (eql subchar #\") (error "Subchar must be doublequote."))
>   (unread-char subchar stream)
>   (let ((format-string (read stream t nil t)))
>     `(formatter ,format-string)))

I was wondering if it would be better to return
`(lambda (stream &rest args)
   (format stream "~?" string args))

They are almost equivalent, but the former requires

(funcall #"blark ~d" 44)

while the latter allows

(#"blark ~d" 44)
From: Kent M Pitman
Subject: Re: Read macro returning function?
Date: 
Message-ID: <sfwit5qb9vr.fsf@shell01.TheWorld.com>
"Joe Marshall" <·············@attbi.com> writes:

> "Kent M Pitman" <······@world.std.com> wrote in message ····················@shell01.TheWorld.com...
> > Nils Goesche <······@cartan.de> writes:
> >
> > > In some paper I read the author assumed the existence of a read macro
> > > that would turn e.g. #"blark ~D" into a function that takes a stream
> > > and an integer and prints it like FORMAT would.
> >
> > I'm ignoring the rest of your post as irrelevant because you've misstated
> > the problem.
> >
> > Readmacros should return program expressions not data (where a first class
> > function is data).
> >
> > (defun read-sharp-doublequote (stream subchar param)
> >   (declare (ignore param subchar))
> >   (unless (eql subchar #\") (error "Subchar must be doublequote."))
> >   (unread-char subchar stream)
> >   (let ((format-string (read stream t nil t)))
> >     `(formatter ,format-string)))
> 
> I was wondering if it would be better to return
> `(lambda (stream &rest args)
>    (format stream "~?" string args))
> 
> They are almost equivalent, but the former requires
> 
> (funcall #"blark ~d" 44)
> 
> while the latter allows
> 
> (#"blark ~d" 44)

Actually, the intended use is:

 (format t #"blark ~d" 44)

Note that FORMAT was changed in ANSI CL to take a 'format control' instead
of a 'format string'.  Consequently, it also works to pass these functions
in places where an error string would seem to be called for, which is why
use use the :FORMAT-CONTROL keyword to SIMPLE-ERROR, etc.

So there are quite a number of other uses than the thing you're thinking of.

Now, it's true that if you add the extra layer of LAMBDAness, you do get to
pun on namespacing, and I suppose you could call that a feature.  You may
introduce some extra consing.  I might, for example, at least want to see
you (declare (dynamic-extent args)) in your expansion.  I dunno.
It's a judgment call, I guess.

It's a pity that CL didn't want what the LispM called 'lambda macros' because
then one could have made ((formatter ...) ...) work in the function namespace,
too.  But people thought that was 'overkill'.
From: james anderson
Subject: Re: Read macro returning function?
Date: 
Message-ID: <3CE25DDE.CB68CAF8@setf.de>
Kent M Pitman wrote:
> 
> Nils Goesche <······@cartan.de> writes:
> 
> > In some paper I read the author assumed the existence of a read macro
> > that would turn e.g. #"blark ~D" into a function that takes a stream
> > and an integer and prints it like FORMAT would.
> 
> I'm ignoring the rest of your post as irrelevant because you've misstated
> the problem.
> 
> Readmacros should return program expressions not data (where a first class
> function is data).
> 

why this restriction?
From: Joe Marshall
Subject: Re: Read macro returning function?
Date: 
Message-ID: <fHtE8.5248$Bn5.1982005@typhoon.ne.ipsvc.net>
"james anderson" <··············@setf.de> wrote in message ······················@setf.de...
>
>
> Kent M Pitman wrote:
> >
> > Readmacros should return program expressions not data (where a first class
> > function is data).
> >
>
> why this restriction?

I don't want to put words in Kent's mouth, but surely he didn't mean to
exclude such things as literal lists, arrays, numbers, structures, etc.
from either the class of `data' or the class of things that readmacros
ought to return.

In the case of  #"blark ~D", he is correct that it ought to
return an *expression* rather than a *function*.  The job of the reader
is to convert a printed representation to a structured one, not to
interpret what that structure might mean (that's the job of EVAL or
the compiler).  The reader doesn't have enough information to do
that correctly.

It also breaks read-print-read consistency, and would cause problems
with anything that needs to analyze code, like macros and code-walkers.
From: james anderson
Subject: Re: Read macro returning function?
Date: 
Message-ID: <3CE27BF1.5A1987EC@setf.de>
Joe Marshall wrote:
> 
> "james anderson" <··············@setf.de> wrote in message ······················@setf.de...
> >
> >
> > Kent M Pitman wrote:
> > >
> > > Readmacros should return program expressions not data (where a first class
> > > function is data).
> > >
> >
> > why this restriction?
> 
> I don't want to put words in Kent's mouth, but surely he didn't mean to
> exclude such things as literal lists, arrays, numbers, structures, etc.
> from either the class of `data' or the class of things that readmacros
> ought to return.
> 
> In the case of  #"blark ~D", he is correct that it ought to
> return an *expression* rather than a *function*.  The job of the reader
> is to convert a printed representation to a structured one, not to
> interpret what that structure might mean (that's the job of EVAL or
> the compiler).  The reader doesn't have enough information to do
> that correctly.
> 
> It also breaks read-print-read consistency, and would cause problems
> with anything that needs to analyze code, like macros and code-walkers.

according to which the restriction may be relaxed to permit lisp objects which
- can be constructed in the reader's environment,
- are intended to be opaque wrt deferred code analysis, and
- have a readable print form?
From: Joe Marshall
Subject: Re: Read macro returning function?
Date: 
Message-ID: <RPvE8.5612$Bn5.2025639@typhoon.ne.ipsvc.net>
"james anderson" <··············@setf.de> wrote in message ······················@setf.de...
>
>
> Joe Marshall wrote:
> >
> > "james anderson" <··············@setf.de> wrote in message ······················@setf.de...
> > >
> > >
> > > Kent M Pitman wrote:
> > > >
> > > > Readmacros should return program expressions not data (where a first class
> > > > function is data).
> > > >
> > >
> > > why this restriction?
> >
> > I don't want to put words in Kent's mouth, but surely he didn't mean to
> > exclude such things as literal lists, arrays, numbers, structures, etc.
> > from either the class of `data' or the class of things that readmacros
> > ought to return.
> >
> > In the case of  #"blark ~D", he is correct that it ought to
> > return an *expression* rather than a *function*.  The job of the reader
> > is to convert a printed representation to a structured one, not to
> > interpret what that structure might mean (that's the job of EVAL or
> > the compiler).  The reader doesn't have enough information to do
> > that correctly.
> >
> > It also breaks read-print-read consistency, and would cause problems
> > with anything that needs to analyze code, like macros and code-walkers.
>
> according to which the restriction may be relaxed to permit lisp objects which
> - can be constructed in the reader's environment,
> - are intended to be opaque wrt deferred code analysis, and
> - have a readable print form?

Yep.
Or will be transparent wrt deferred code analysis, like a syntactically
correct s-exp.
From: Kent M Pitman
Subject: Re: Read macro returning function?
Date: 
Message-ID: <sfwoffhhtcj.fsf@shell01.TheWorld.com>
"Joe Marshall" <·············@attbi.com> writes:

> "james anderson" <··············@setf.de> wrote in message ······················@setf.de...
> >
> >
> > Kent M Pitman wrote:
> > >
> > > Readmacros should return program expressions not data (where a first class
> > > function is data).
> > >
> >
> > why this restriction?
> 
> I don't want to put words in Kent's mouth, but surely he didn't mean to
> exclude such things as literal lists, arrays, numbers, structures, etc.
> from either the class of `data' or the class of things that readmacros
> ought to return.

Well, such data are programs.  They just self-evaluate.

But given a choice between returning a non-externalizable datum and a
program form, I'd do the latter unless I knew the readmacro was not for
use in programs.

> In the case of  #"blark ~D", he is correct that it ought to
> return an *expression* rather than a *function*.  The job of the reader
> is to convert a printed representation to a structured one, not to
> interpret what that structure might mean (that's the job of EVAL or
> the compiler).  The reader doesn't have enough information to do
> that correctly.
> 
> It also breaks read-print-read consistency, and would cause problems
> with anything that needs to analyze code, like macros and code-walkers.

Yes, if you do it my way, you can even set up (formatter "foo") to 
pretty-print as #"foo"
From: Nils Goesche
Subject: Re: Read macro returning function?
Date: 
Message-ID: <lkwuu5cvd8.fsf@pc022.bln.elmeg.de>
Kent M Pitman <······@world.std.com> writes:

> Nils Goesche <······@cartan.de> writes:
> 
> > In some paper I read the author assumed the existence of a read macro
> > that would turn e.g. #"blark ~D" into a function that takes a stream
> > and an integer and prints it like FORMAT would.
> 
> I'm ignoring the rest of your post as irrelevant because you've misstated
> the problem.
> 
> Readmacros should return program expressions not data (where a first class
> function is data).

Hm, didn't my second version do just that?  Here is it again:

(defun define-format-macro2 ()
  (let ((string-reader (get-macro-character #\")))
    (flet ((reader-func (stream sub param)
             (declare (ignore param))
             (let ((string (funcall string-reader stream sub)))
               `(lambda (stream &rest args)
                  (apply #'format stream ,string args)))))
      (set-dispatch-macro-character #\# #\" #'reader-func))))

Isn't that fine, too then?

> (defun read-sharp-doublequote (stream subchar param)
>   (declare (ignore param subchar))
>   (unless (eql subchar #\") (error "Subchar must be doublequote."))
>   (unread-char subchar stream)
>   (let ((format-string (read stream t nil t)))
>     `(formatter ,format-string)))
> 
> (set-dispatch-macro-character #\# #\" 'read-sharp-doublequote)
> 
> (funcall #"foo ~X" *standard-output* 37)
> foo 25
> => NIL

Aargh, I had completely forgotten about FORMATTER, thanks for the
hints, everyone!

Regards,
-- 
Nils Goesche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x42B32FC9