Hello.
To understand macros better I thought that I should write a macro which
is more than just "Hello world", so I wrote this macro. The idea is
that nesting function calls sometimes get ugly, so some sort of
chaining could be useful.
e.g.:
(reverse
(let ((str (string-downcase
(reverse (subseq "I LIKE MACROS." 7 13)))))
(format nil "~a >-< ~a" (reverse str) str)))
will be written like:
(chain "I LIKE MACROS."
(subseq _ 7 13)
(reverse)
(string-downcase)
(format nil "~a >-< ~a" (reverse _) _)
(reverse))
- Do you see any problems with this?
- Do you find it useful?
- Am I reinventing the wheel (I didn't find anything like this at
google)
- Comments about the code are welcome
And the code:
(defun count-atom (expr my-atom)
"Count the number of 'my-atom's in 'expr'"
(cond
((eq expr my-atom) 1)
((atom expr) 0)
(t (+ (count-atom (car expr) my-atom)
(count-atom (cdr expr) my-atom)))))
(defun mtfo-one-method (sub main placeholder)
"Macro helper that helps converting the next cases:
(expr (funct)) => (funct expr)
(expr (func (list _))) => (func (list expr))
(expr (func _ (list _))) => (let ((#:G1604 expr)) (func #:G1604
(list #:G1604)))
In the previous examples sub is 'expr', main is the list after
'expr',
and placeholder is _."
(if (atom main)
(error (format nil "Function was expected in the place of ~a"
main))
(let ((count (count-atom main placeholder)))
(cond
((= count 0)
(if (null (cdr main))
(list (car main) sub)
(error
(format nil "~a must contain at least one ~a when it
contains parameters."
main placeholder))))
((= count 1) (nsubst sub placeholder main))
(t (let ((the-value (gensym)))
`(let ((,the-value ,sub))
,(nsubst the-value placeholder main))))))))
(defun chain-to-function (exps placeholder)
(cond
((null exps) nil)
(t (flet ((chainer (x y)
(mtfo-one-method x y placeholder)))
(reduce #'chainer exps)))))
(defmacro chain (&body body)
(if (and (> (length body) 1) (eq (car body) :placeholder))
(chain-to-function (cddr body) (cadr body))
(chain-to-function body '_)))
; Elegancy test :)
(chain "I LIKE MACROS."
(subseq _ 7 13)
(reverse)
(string-downcase)
(format t "Soru ~a~%" _))
; Complexity test :)
(chain (chain "I LIKE MACROS."
(subseq _ 7 12)
(reverse)
(format nil "Bir soru s~a, ~a nedir? :)" _ (reverse _)))
(string-capitalize)
(format nil " *** ~a *** " _)
((lambda (x) `(,x ,(reverse x))))
`(,(car _) " " ,(cadr _)))
; placeholder test
(let ((_ 100))
(chain :placeholder :p
40
(+ :p 9)
(sqrt)
(+ :p _)))
"Mehmet Yavuz S. Soyturk" <··················@gmail.com> wrote in message
·····························@f14g2000cwb.googlegroups.com...
> Hello.
>
> To understand macros better I thought that I should write a macro which
> is more than just "Hello world", so I wrote this macro. The idea is
> that nesting function calls sometimes get ugly, so some sort of
> chaining could be useful.
>
> e.g.:
>
> (reverse
> (let ((str (string-downcase
> (reverse (subseq "I LIKE MACROS." 7 13)))))
> (format nil "~a >-< ~a" (reverse str) str)))
>
> will be written like:
>
> (chain "I LIKE MACROS."
> (subseq _ 7 13)
> (reverse)
> (string-downcase)
> (format nil "~a >-< ~a" (reverse _) _)
> (reverse))
>
> - Do you see any problems with this?
No.
> - Do you find it useful?
No, I'm used to parsing lisp code, and if
it gets really nasty, there's usually an
idea for something simpler trying to get
out.
> - Am I reinventing the wheel (I didn't find anything like this at
> google)
Wrong group, see comp.lang.forth and comp.lang.postscript :-)
The only thing you're missing is a stack.
--
Geoff
Mehmet Yavuz S. Soyturk <··················@gmail.com> wrote:
+---------------
| ...nesting function calls sometimes get ugly,
| so some sort of chaining could be useful.
+---------------
You might want to look at the COMPOSE function:
http://www.cliki.net/COMPOSE
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
Rob Warnock wrote:
> Mehmet Yavuz S. Soyturk <··················@gmail.com> wrote:
> +---------------
> | ...nesting function calls sometimes get ugly,
> | so some sort of chaining could be useful.
> +---------------
>
> You might want to look at the COMPOSE function:
>
> http://www.cliki.net/COMPOSE
Is there any reason that that page contains a compiler macro, instead of
a normal macro? I'm not sure if they are portable, and since I've never
before seen one, I'd feel a little uncomfortable using that definition
in my Lisp. OTOH, I'd prefer a macro over a function, since COMPOSE is
an obvious candidate for manual inlining.
--
Suffering from Gates-induced brain leakage...
From: Edi Weitz
Subject: Re: Macro: chaining function calls.
Date:
Message-ID: <u64nokh8b.fsf@agharta.de>
On Thu, 09 Feb 2006 10:22:46 +0100, Ulrich Hobelmann <···········@web.de> wrote:
> Is there any reason that that page contains a compiler macro,
> instead of a normal macro?
Yes, you yourself mention the reason in your last sentence.
> I'm not sure if they are portable, and since I've never before seen
> one, I'd feel a little uncomfortable using that definition in my
> Lisp.
They're part of the ANSI standard, so they are portable by definition.
Your implementation is free to ignore compiler macros, but if it does
so consistently, you should probably dump it anyway... :)
> OTOH, I'd prefer a macro over a function, since COMPOSE is an
> obvious candidate for manual inlining.
See above.
--
Lisp is not dead, it just smells funny.
Real email: (replace (subseq ·········@agharta.de" 5) "edi")
Edi Weitz wrote:
> They're part of the ANSI standard, so they are portable by definition.
> Your implementation is free to ignore compiler macros, but if it does
> so consistently, you should probably dump it anyway... :)
Ok :)
>> OTOH, I'd prefer a macro over a function, since COMPOSE is an
>> obvious candidate for manual inlining.
>
> See above.
But in what way wouldn't a normal macro expanding (COMPOSE #'a #'b ...)
to #'(lambda (x) (a (b ... x))) do that inlining too? Wherever it's
called at compile time, it would expand to the same lambda and the
compiler should create and optimize it like a "normal" function (which
you could then pass to MAPCAR or whatever...).
I can't quite see what a compiler macro could do in this case that the
compiler wouldn't do anyway.
--
Suffering from Gates-induced brain leakage...
Ulrich Hobelmann wrote:
> Edi Weitz wrote:
>
>> They're part of the ANSI standard, so they are portable by definition.
>> Your implementation is free to ignore compiler macros, but if it does
>> so consistently, you should probably dump it anyway... :)
>
> Ok :)
>
>>> OTOH, I'd prefer a macro over a function, since COMPOSE is an
>>> obvious candidate for manual inlining.
>>
>>
>> See above.
>
> But in what way wouldn't a normal macro expanding (COMPOSE #'a #'b ...)
> to #'(lambda (x) (a (b ... x))) do that inlining too? Wherever it's
> called at compile time, it would expand to the same lambda and the
> compiler should create and optimize it like a "normal" function (which
> you could then pass to MAPCAR or whatever...).
>
> I can't quite see what a compiler macro could do in this case that the
> compiler wouldn't do anyway.
If you can implement something as a function, it's better to implement
it as a function in the general case. The reason is that you can pass a
function as a first-class value, while you cannot do this with a macro,
at least not in a straightforward way. It could be that someone wants to
pass #'compose around.
ANSI Common Lisp doesn't require an implementation to do any automatic
inlining of function calls. An implementation is allowed to completely
ignore inline declarations. (It is not allowed to ignore notinline
declarations, though.)
So if you define a compiler-macro for a function, you increase the
chances that your function gets indeed inlined, for example in
implementations that don't inline functions but make use of
compiler-macros. (If I understand correctly, Allegro Common Lisp is such
an implementation.) This is especially a worthwhile consideration in
code that is supposed to be portable.
Pascal
--
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
Pascal Costanza wrote:
> If you can implement something as a function, it's better to implement
> it as a function in the general case. The reason is that you can pass a
> function as a first-class value, while you cannot do this with a macro,
> at least not in a straightforward way. It could be that someone wants to
> pass #'compose around.
Makes sense, yes.
> ANSI Common Lisp doesn't require an implementation to do any automatic
> inlining of function calls. An implementation is allowed to completely
> ignore inline declarations. (It is not allowed to ignore notinline
> declarations, though.)
I didn't really mean inlining of the function, but combining all the
calls in a single function's body, as a lambda function. This will end
up as assembly code with just a few explicit call instructions, with
known targets.
I would use COMPOSE for instance to create a function to pass to MAPCAR.
I don't see where you could do more there, than make a single function
out of COMPOSE's arguments. Certainly that's much faster than
FUNCALLing them again and again.
> So if you define a compiler-macro for a function, you increase the
> chances that your function gets indeed inlined, for example in
> implementations that don't inline functions but make use of
> compiler-macros. (If I understand correctly, Allegro Common Lisp is such
> an implementation.) This is especially a worthwhile consideration in
> code that is supposed to be portable.
I think we mean different senses of inlining. I strongly suspect that
Allegro would compile lambda bodies, just as it compiles named DEFUN
functions.
--
Suffering from Gates-induced brain leakage...
On 9380 day of my life Ulrich Hobelmann wrote:
>>> OTOH, I'd prefer a macro over a function, since COMPOSE is an
>>> obvious candidate for manual inlining.
>> See above.
>
> But in what way wouldn't a normal macro expanding (COMPOSE #'a #'b
> ...) to #'(lambda (x) (a (b ... x))) do that inlining too? Wherever
> it's called at compile time, it would expand to the same lambda and
> the compiler should create and optimize it like a "normal" function
> (which you could then pass to MAPCAR or whatever...).
COMPOSE is much more useful as function than as macro. For example:
(mapcar (apply #'compose list-of-function)
list-of-data)
But you can't use APPLY with macros.
Compiler macros allow you to get best of both worlds: you can use
COMPOSE as function (since defun is provided) and some calls of
COMPOSE are optimized.
There was some report on using compiler-macros... Try google.
--
Ivan Boldyrev
Assembly of a Japanese bicycle requires greatest peace of spirit.
> COMPOSE is much more useful as function than as macro. For example:
>
> (mapcar (apply #'compose list-of-function)
> list-of-data)
>
> But you can't use APPLY with macros.
>
> Compiler macros allow you to get best of both worlds: you can use
> COMPOSE as function (since defun is provided) and some calls of
> COMPOSE are optimized.
>
> There was some report on using compiler-macros... Try google.
Generally I agree with you. Compiler macros are the right tool for
compose.
P.S. You can apply macros, it's just not as easy as with functions.
It's almost the same as with implementing apply with funcall.
On 9383 day of my life ······@thedoghousemail.com wrote:
> P.S. You can apply macros, it's just not as easy as with functions.
> It's almost the same as with implementing apply with funcall.
Do you mean something like this?
(defun simplified-apply (func args)
(eval `(funcall ,function ,@args)))
But same idea wouldn't work with macros: APPLY doesn't use lexical
environment, while macros do.
Yes, you can implement COMPOSE with macro that doesn't use lexical
environment explicitly. But it is because COMPOSE has nature of
function... General macro will not work.
Or do you mean something else?
--
Ivan Boldyrev
Is 'morning' a gerund?
Ivan Boldyrev wrote:
> Do you mean something like this?
>
> (defun simplified-apply (func args)
> (eval `(funcall ,function ,@args)))
No,I mean something like
http://www.hexapodia.net/pipermail/small-cl-src/2004-June/000015.html
On 9387 day of my life ······@thedoghousemail.com wrote:
>> Do you mean something like this?
>>
>> (defun simplified-apply (func args)
>> (eval `(funcall ,function ,@args)))
>
> No,I mean something like
> http://www.hexapodia.net/pipermail/small-cl-src/2004-June/000015.html
Horror!!! :)))
--
Ivan Boldyrev
| recursion, n:
| See recursion
Ivan Boldyrev wrote:
> COMPOSE is much more useful as function than as macro. For example:
>
> (mapcar (apply #'compose list-of-function)
> list-of-data)
>
> But you can't use APPLY with macros.
That looks like a cool application, but as long as I don't need it...
I think it's best to have both COMPOSEs around, one when you need it,
and the other to create *efficient* functions when the arguments are known.
> Compiler macros allow you to get best of both worlds: you can use
> COMPOSE as function (since defun is provided) and some calls of
> COMPOSE are optimized.
Oh, cool. :)
--
Suffering from Gates-induced brain leakage...
From: Edi Weitz
Subject: Re: Macro: chaining function calls.
Date:
Message-ID: <uirrkrdnz.fsf@agharta.de>
On Sun, 12 Feb 2006 12:07:19 +0100, Ulrich Hobelmann <···········@web.de> wrote:
> That looks like a cool application, but as long as I don't need
> it... I think it's best to have both COMPOSEs around, one when you
> need it, and the other to create *efficient* functions when the
> arguments are known.
You want compiler macros. That's what we're trying to tell you all
the time...
--
European Common Lisp Meeting 2006: <http://weitz.de/eclm2006/>
Real email: (replace (subseq ·········@agharta.de" 5) "edi")
Edi Weitz wrote:
> On Sun, 12 Feb 2006 12:07:19 +0100, Ulrich Hobelmann <···········@web.de> wrote:
>
>> That looks like a cool application, but as long as I don't need
>> it... I think it's best to have both COMPOSEs around, one when you
>> need it, and the other to create *efficient* functions when the
>> arguments are known.
>
> You want compiler macros. That's what we're trying to tell you all
> the time...
By now I got it. ;)
I dug out the following blog entry on Google:
http://cooking-with-lisp.blogspot.com/2004/07/trying-to-grok-compiler-macros.html
It contains a link to another paper I'm gonna read, as well as a concise
explanation of the essence of compiler macros.
--
Suffering from Gates-induced brain leakage...
Ulrich Hobelmann <···········@web.de> writes:
> > http://www.cliki.net/COMPOSE
> Is there any reason that that page contains a compiler macro, instead of
> a normal macro? I'm not sure if they are portable, and since I've never
> before seen one,
You should not be afrain of learning something new in CL.
> OTOH, I'd prefer a macro over a function, since COMPOSE is
> an obvious candidate for manual inlining.
Please search the web for a couple of excellent articles on compiler
macros. Basically:
1. There's the function COMPOSE, which you can APPLY/FUNCALL etc.
2. When you want to optimize literal uses of compose, you call for
macros. You shouldn't. Compiler macros jump in and allow exactly
this optimization of literal invocations of COMPOSE (inlining).
Bonus: the function is still there.
Compiler macros are portable, since they are in ANSI-CL. They may not
be invoked, but we were talking about optimizations, didn't we?
Show me an optimizing compiler that does not follow compiler macros.
CFFI is an example where the authors systematically preserved this
beautiful separation of concern. The API is full of functions (unlike
UFFI), yet many literal function calls get optimized away via compiler
macros.
Repeat ten times :) Compiler macros are a great idea!
Regards
Jorg Hohle
Telekom/T-Systems Technology Center
Mehmet Yavuz S. Soyturk wrote:
> Hello.
>
> To understand macros better I thought that I should write a macro which
> is more than just "Hello world", so I wrote this macro. The idea is
> that nesting function calls sometimes get ugly, so some sort of
> chaining could be useful.
This is basically like piping in the UNIX shells.
I made a macro similar to yours in 2002. I reposted it here last
November.
See message ID: ·······················@o13g2000cwo.googlegroups.com
OR:
http://groups.google.com/group/comp.lang.lisp/msg/68b08ac1c8ec4f20
It has the same support for placing the previous pipeline element into
any position in the argument list using the underscore symbol. But
there are some additional bells and whistles: handling multiple values
coming out of pipeline stages, splitting lists and multiple values
between parallel pipelines, and such. Also, there is special syntax for
some functions which eliminates LAMBDA, as well as a mechanism by which
the user can extend this to include new functions. To map a list
through an expression you can write something like:
(mapcar (+ 2))
will assume that a list came out of the previous pipeline element, and
it will make a new list by adding 2 to each element. I.e. you are
spared from writing:
(mapcar #'(lambda (x) (+ 2 x)))
The argument X of the hidden lambda is called _ of course.