From: howard yeh
Subject: Lisp Reader Modification and Macro
Date: 
Message-ID: <1169840739.025201.24580@l53g2000cwa.googlegroups.com>
Hi,

I am practicing my MOPfu by writing a mock ruby object system
(rooby). Just for the chance to practice lisp reader programming, I
want to have the dot-notation, like so:

(length. (to_str. 10))

would be equivalent to

10.to_str.length


But then, I don't know how to make that work with the macro, as the
reader would translate (msg. data) to (send data 'msg) at read time.

I suppose this is the same problem as trying to embed Lisp-1 in CL,
you need a code walker.

There must be an easier way.

If only CL (the language) has a metaprotocol like MOP... then it would
be so easy. Anybody has one lying around?

Howard

PS- if you have a mock ruby lying around, I would want to look at it
too!

From: Kaz Kylheku
Subject: Re: Lisp Reader Modification and Macro
Date: 
Message-ID: <1169845300.995697.188000@a75g2000cwd.googlegroups.com>
On Jan 26, 11:45 am, "howard yeh" <······@gmail.com> wrote:
> Hi,
>
> I am practicing my MOPfu by writing a mock ruby object system
> (rooby). Just for the chance to practice lisp reader programming, I
> want to have the dot-notation, like so:
>
> (length. (to_str. 10))

[ sip ]

> I suppose this is the same problem as trying to embed Lisp-1 in CL,
> you need a code walker.

In all you learning about MOP, Lisp-1, code walkers, etc. you have
somehow managed to overlook that Lisp already has a dot notation. It's
a bad idea to hijack that notation with a read macro.

> PS- if you have a mock ruby lying around, I would want to look at it
> too!

If you want something to mock, there is plenty of material at
www.ruby-lang.org. Mailing list archives, documentation, etc.
From: howard yeh
Subject: Re: Lisp Reader Modification and Macro
Date: 
Message-ID: <1169856325.428913.82380@p10g2000cwp.googlegroups.com>
> somehow managed to overlook that Lisp already has a dot notation. It's
> a bad idea to hijack that notation with a read macro.

Ya, but the dot notation is used by itself, not as symbol constituent.
I only wanted "symbol." to mean a special class of symbol, like
":symbol" would mean a keyword. Anyway, let's just say I want to use
"$" instead.

(length$ (to_str$ 10))

I think my question is still worth asking. If you modify the reader,
how can you make it work with macros? CLSQL has a reader syntax, but
it's impossible to use macro with it. The answer is probably to use a
full code walker. But maybe there's a simpler way?
From: Kaz Kylheku
Subject: Re: Lisp Reader Modification and Macro
Date: 
Message-ID: <1169873192.907427.304760@j27g2000cwj.googlegroups.com>
On Jan 26, 4:05 pm, "howard yeh" <······@gmail.com> wrote:
> > somehow managed to overlook that Lisp already has a dot notation. It's
> > a bad idea to hijack that notation with a read macro.
>
> Ya, but the dot notation is used by itself, not as symbol constituent.

I see; you do want to use it as a non-terminating macro character. But
you already have that.

> I only wanted "symbol." to mean a special class of symbol, like

If you really want (a. b) to translate to (send b 'a), why don't you
write a macro named a. to do the job? Or a macro which writes that
macro, so you can define a whole bunch of them all at once?

> ":symbol" would mean a keyword. Anyway, let's just say I want to use
> "$" instead.
>
> (length$ (to_str$ 10))

This really just looks like you're trying to have your own namespace.
You can do that with packages. It's not clear what this has to do with
the member selection infix dot operator in other languages.

Why does (length$ x) have to be translated to (send x 'length) at read
time, or even macro-expansion time? Why can't length$ just be an
(inline) function, which wraps the call to send?

  (declaim (inline length$))

  (defun length$ (obj)
    (send obj 'length))

You could make a macro which takes an arbitrarily long list of symbols
as its argument, and writes the above wrappers for each of those
symbols, so that:

  (define-$-language length to_str ...)

writes the functions length$, to_str$ and so on.

> If you modify the reader, how can you make it work with macros?

Well, since you have chosen to do something which basically /is/ macro
expansion in the reader, of course it doesn't play along nicely with
macros, since read syntax isn't the target language of macros.

> CLSQL has a reader syntax, but it's impossible to use macro with it.

Reader syntaxes can be used with macros if their transformation is to
some reasonable target language, such that if something behaves like a
call to function X in the read syntax, it translates to an (X ...)
expression that is evaluated normally. Then a macro X can be written to
hook into that.

Your problem is that you are targettijng the (send ...) API directly at
read time rather than encapsulating it behind a function-call like
target language. That's the only kind of target language for which Lisp
will give you straightforward macro support.

Another alternative is to do something similar to SETF expanders. Call
them SEND expanders. You turn SEND into a large, extensible macro, and
provide some API for writing the subordinate macros: the expanders,
analogous to what macros like DEFSETF and DEFINE-SETF-EXPANDER to for
SETF.

So then you could in fact write some custom expansion for (length.
obj).

This translates to (send obj length), which is a macro call. The SEND
macro recognizes that it has a LENGTH handler in its expander database,
and so it calls on that to produce the expansion, and off you go.
From: howard yeh
Subject: Re: Lisp Reader Modification and Macro
Date: 
Message-ID: <1169916021.911354.159740@h3g2000cwc.googlegroups.com>
On Jan 26, 8:46 pm, "Kaz Kylheku" <········@gmail.com> wrote:
> On Jan 26, 4:05 pm, "howard yeh" <······@gmail.com> wrote:
>
> > > somehow managed to overlook that Lisp already has a dot notation. It's
> > > a bad idea to hijack that notation with a read macro.
>
> > Ya, but the dot notation is used by itself, not as symbol constituent.I see; you do want to use it as a non-terminating macro character. But
> you already have that.
>
> > I only wanted "symbol." to mean a special class of symbol, likeIf you really want (a. b) to translate to (send b 'a), why don't you
> write a macro named a. to do the job? Or a macro which writes that
> macro, so you can define a whole bunch of them all at once?
>

Ah, that's the answer I am looking for. Thanx! Oh, but if I have 
different "classes" imported from different packages, I would have a 
bad case of symbol clashing. If I have the Army and Array classes, 
they both have "join" defined as a method, what would be nice is if 
each of them has its own namespace (duck-typing). I think this is the 
namespace problem you referred to later.

> > ":symbol" would mean a keyword. Anyway, let's just say I want to use
> > "$" instead.
>
> > (length$ (to_str$ 10))This really just looks like you're trying to have your own namespace.
> You can do that with packages. It's not clear what this has to do with
> the member selection infix dot operator in other languages.
>
> Why does (length$ x) have to be translated to (send x 'length) at read
> time, or even macro-expansion time? Why can't length$ just be an
> (inline) function, which wraps the call to send?
>
>   (declaim (inline length$))
>
>   (defun length$ (obj)
>     (send obj 'length))
>
> You could make a macro which takes an arbitrarily long list of symbols
> as its argument, and writes the above wrappers for each of those
> symbols, so that:
>
>   (define-$-language length to_str ...)
>
> writes the functions length$, to_str$ and so on.
>
> > If you modify the reader, how can you make it work with macros?Well, since you have chosen to do something which basically /is/ macro
> expansion in the reader, of course it doesn't play along nicely with
> macros, since read syntax isn't the target language of macros.
>
> > CLSQL has a reader syntax, but it's impossible to use macro with it.Reader syntaxes can be used with macros if their transformation is to
> some reasonable target language, such that if something behaves like a
> call to function X in the read syntax, it translates to an (X ...)
> expression that is evaluated normally. Then a macro X can be written to
> hook into that.
>
> Your problem is that you are targettijng the (send ...) API directly at
> read time rather than encapsulating it behind a function-call like
> target language. That's the only kind of target language for which Lisp
> will give you straightforward macro support.
>
> Another alternative is to do something similar to SETF expanders. Call
> them SEND expanders. You turn SEND into a large, extensible macro, and
> provide some API for writing the subordinate macros: the expanders,
> analogous to what macros like DEFSETF and DEFINE-SETF-EXPANDER to for
> SETF.
>
> So then you could in fact write some custom expansion for (length.
> obj).
>
> This translates to (send obj length), which is a macro call. The SEND
> macro recognizes that it has a LENGTH handler in its expander database,
> and so it calls on that to produce the expansion, and off you go.

About needing a good api for macro manipulation is a good point. I 
think that solidifies the question a lot for me.
Just my idle thoughts... perhaps it would be good to have /yet 
another/ stage of macro expansion. It seems that reader macro should 
simply be avoided because it messes up the textual representation 
before the programmer has a chance to manipulate it.

I think it might be useful to have two classes of macros (beyond 
reader macro, which we really shouldn't touch): user-level and 
designer-level. Or onstage and offstage in MOP's terms. User-level 
macros are what we have now, where transformation is local (in the 
sense that you don't have to recurse down the body of the macro). But 
in many cases, when you want to embed a DSL, you would often need 
global transformations of the program text. The message passing syntax 
is one example. Or any cases where you might want to interpret the 
functional position differently.

Lisp-1, or, an html dialect for which a keyword at the function 
position means to output that tag.

(:div 10) => <div> 10 </div>

The designer macros would be a series of tree transformations, with 
the AST represented with CLOS objects. So defining lisp-1, message-
passing, or htmlisp would be as simple as defining the methods to do 
the transformations of functional positions (after user had a chance 
to manipulate the program text with macro, so users can glibly put 
keywords in the function position for htmlisp, for example).
From: Pascal Costanza
Subject: Re: Lisp Reader Modification and Macro
Date: 
Message-ID: <51vkttF1if17sU1@mid.individual.net>
howard yeh wrote:
>> somehow managed to overlook that Lisp already has a dot notation. It's
>> a bad idea to hijack that notation with a read macro.
> 
> Ya, but the dot notation is used by itself, not as symbol constituent.
> I only wanted "symbol." to mean a special class of symbol, like
> ":symbol" would mean a keyword. Anyway, let's just say I want to use
> "$" instead.
> 
> (length$ (to_str$ 10))
> 
> I think my question is still worth asking. If you modify the reader,
> how can you make it work with macros? CLSQL has a reader syntax, but
> it's impossible to use macro with it. The answer is probably to use a
> full code walker. But maybe there's a simpler way?

(defmethod length$ ((str string))
   (length str))

(defmethod to_str$ (obj)
   (format nil "~A" obj))

 > (length$ (to_str$ 10))
2


?!?


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: howard yeh
Subject: Re: Lisp Reader Modification and Macro
Date: 
Message-ID: <1169914263.536383.234930@k78g2000cwa.googlegroups.com>
On Jan 26, 4:33 pm, Pascal Costanza <····@p-cos.net> wrote:
> howard yeh wrote:
> >> somehow managed to overlook that Lisp already has a dot notation. It's
> >> a bad idea to hijack that notation with a read macro.
>
> > Ya, but the dot notation is used by itself, not as symbol constituent.
> > I only wanted "symbol." to mean a special class of symbol, like
> > ":symbol" would mean a keyword. Anyway, let's just say I want to use
> > "$" instead.
>
> > (length$ (to_str$ 10))
>
> > I think my question is still worth asking. If you modify the reader,
> > how can you make it work with macros? CLSQL has a reader syntax, but
> > it's impossible to use macro with it. The answer is probably to use a
> > full code walker. But maybe there's a simpler way?(defmethod length$ ((str string))
>    (length str))
>
> (defmethod to_str$ (obj)
>    (format nil "~A" obj))
>
>  > (length$ (to_str$ 10))
> 2
>
> ?!?

Yes. But that doesn't work quite as nicely as duck typing in managing 
accessible symbols.

Suppose you want different parameter lists? You could use &rest, then 
parse parameter yourself, and as I understand it, that's not how it's 
meant to be used. You might want:

(join. army person pledge)
(join. str-array ",")

But the usual lisp idiom (again, as far as i can observe) is

(army-join army person pledge)
(string-join str-array ",")

Where the two may come from two different packages, and have to be 
named thus to avoid clashing when imported.
In my mind, the nicety of duck-typing over generic functions is simply 
the namespace management.

>
> Pascal
>
> --
> My website:http://p-cos.net
> Common Lisp Document Repository:http://cdr.eurolisp.org
> Closer to MOP & ContextL:http://common-lisp.net/project/closer/
From: Pascal Costanza
Subject: Re: Lisp Reader Modification and Macro
Date: 
Message-ID: <521hqmF1lnidoU1@mid.individual.net>
howard yeh wrote:
> 
> On Jan 26, 4:33 pm, Pascal Costanza <····@p-cos.net> wrote:
>> howard yeh wrote:
>>>> somehow managed to overlook that Lisp already has a dot notation. It's
>>>> a bad idea to hijack that notation with a read macro.
>>> Ya, but the dot notation is used by itself, not as symbol constituent.
>>> I only wanted "symbol." to mean a special class of symbol, like
>>> ":symbol" would mean a keyword. Anyway, let's just say I want to use
>>> "$" instead.
>>> (length$ (to_str$ 10))
>>> I think my question is still worth asking. If you modify the reader,
>>> how can you make it work with macros? CLSQL has a reader syntax, but
>>> it's impossible to use macro with it. The answer is probably to use a
>>> full code walker. But maybe there's a simpler way?(defmethod length$ ((str string))
>>    (length str))
>>
>> (defmethod to_str$ (obj)
>>    (format nil "~A" obj))
>>
>>  > (length$ (to_str$ 10))
>> 2
>>
>> ?!?
> 
> Yes. But that doesn't work quite as nicely as duck typing in managing 
> accessible symbols.
> 
> Suppose you want different parameter lists? You could use &rest, then 
> parse parameter yourself, and as I understand it, that's not how it's 
> meant to be used. You might want:
> 
> (join. army person pledge)
> (join. str-array ",")
> 
> But the usual lisp idiom (again, as far as i can observe) is
> 
> (army-join army person pledge)
> (string-join str-array ",")
> 
> Where the two may come from two different packages, and have to be 
> named thus to avoid clashing when imported.
> In my mind, the nicety of duck-typing over generic functions is simply 
> the namespace management.

Maybe, but I think you're attacking this issue from the wrong angle. The 
syntax is a secondary issue (it typically is in Lisp).

You could easily solve your problem by defining a generic function that 
requests all parameters to be passed as required parameters, so you can 
easily specialize on whatever parameter you want. On top of that, you 
define a convenience function that takes optional / key / rest 
parameters and calls the actual generic function for you. It's 
relatively straightforward to provide some macros that automatically 
generate the boiler plate for you. It's also relatively straightforward 
to declare the caller function inline and/or provide compiler macros to 
reduce potential runtime overhead (in case you find out that this is a 
bottleneck in your system).

If this is still not flexible enough (for example because you cannot 
anticipate the set of potentially required arguments in advance) you may 
first want to figure out how the dispatch algorithm should select and 
execute applicable methods, and then implement your own dispatching 
mechanism.

The reader syntax on top of that is just cosmetics.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Richard M Kreuter
Subject: Re: Lisp Reader Modification and Macro
Date: 
Message-ID: <87veisqnd0.fsf@progn.net>
"howard yeh" <······@gmail.com> writes:

> Ya, but the dot notation is used by itself, not as symbol constituent.
> I only wanted "symbol." to mean a special class of symbol, like
> ":symbol" would mean a keyword. Anyway, let's just say I want to use
> "$" instead.
>
> (length$ (to_str$ 10))

While this is "just" syntax, it's worth mentioning that Lisp syntax is
(mostly) designed so that the parser doesn't need need much (if any)
lookaround to determine what's going on:


"abc"    ; double-quote invokes the string reader function

#*11001  ; sharp-asterisk invokes a bit-vector reader function

#(1 2 3) ; sharp-paren invokes an array reader function


Notice that these parsing conventions allow reader subroutines to be
fairly mutually independent, since they don't need to share any hairy
data structures or nontrivial stream use conventions.  This lets you
add new syntax relatively freely.

So instead of putting the syntax that means "this token is fancy" at
the end of the token, put it /first/:

--
;;;; send-readtable.lisp: a trivial readtable for a compressed "send"
;;;; syntax.

;; A stub SEND function, merely for prototyping the syntax.
(defun send (&rest stuff)
  (destructuring-bind (object message &rest arguments) stuff
    (format *standard-output*
	    "Sending message ~S to object ~S with args ~S~%"
	    message object arguments)
    ;; This list is just for simulating the return value of the
    ;; message, so we can see that the send syntax composes.
    (list :sent object message arguments)))

(defun send-reader (stream character)
  (declare (ignore character))
  (let ((sym (read stream)))
    `(lambda (&rest args)
       (let ((thing (first args))
	     (rest (rest args)))
	 (apply #'send thing ',sym rest)))))

(defparameter *my-readtable* (copy-readtable nil))
(set-macro-character #\$ #'send-reader t *my-readtable*)
--

CL-USER> (load "send-readtable")
T
CL-USER> (setq *readtable* *send-readtable*)
#<READTABLE {51008401}>
CL-USER> ($foo '(1 2 3))
Sending message FOO to object (1 2 3) with args NIL
(:SENT (1 2 3) FOO NIL)
CL-USER> ($bar "abc" 4 5 6)
Sending message BAR to object "abc" with args (4 5 6)
(:SENT "abc" BAR (4 5 6))
CL-USER> ($bar ($foo "abc") 4 5 6)
Sending message FOO to object "abc" with args NIL
Sending message BAR to object (:SENT "abc" FOO NIL) with args (4 5 6)
(:SENT (:SENT "abc" FOO NIL) BAR (4 5 6))


--
RmK