From: Mehmet Yavuz S. Soyturk
Subject: Macro: chaining function calls.
Date: 
Message-ID: <1139421236.292996.326430@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?
- 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 _)))

From: Geoffrey Summerhayes
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <2vrGf.4066$J%6.232291@news20.bellglobal.com>
"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
From: Rob Warnock
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <KeudnXowHM8hJnfeRVn-qg@speakeasy.net>
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
From: Ulrich Hobelmann
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <450fv6F4cf8vU1@individual.net>
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")
From: Ulrich Hobelmann
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <450h3eF3vn41U1@individual.net>
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...
From: Pascal Costanza
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <450ingF4bmbeU1@individual.net>
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/
From: Ulrich Hobelmann
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <450mvaF43khbU1@individual.net>
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...
From: Ivan Boldyrev
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <cuq2c3-4rg.ln1@ibhome.cgitftp.uiggm.nsc.ru>
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.
From: leonardo77
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <1139745327.468764.83410@f14g2000cwb.googlegroups.com>
> 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.
From: Ivan Boldyrev
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <shp8c3-7ma.ln1@ibhome.cgitftp.uiggm.nsc.ru>
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?
From: leonardo77
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <1140029023.449783.237300@g43g2000cwa.googlegroups.com>
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
From: Ivan Boldyrev
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <ce0ec3-rvl.ln1@ibhome.cgitftp.uiggm.nsc.ru>
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
From: Ulrich Hobelmann
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <458j77F5dgoqU2@individual.net>
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")
From: Ulrich Hobelmann
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <458uedF5j2r8U2@individual.net>
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...
From: Joerg Hoehle
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <uslq8lt0s.fsf@users.sourceforge.net>
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
From: Kaz Kylheku
Subject: Re: Macro: chaining function calls.
Date: 
Message-ID: <1139477074.690572.3650@g43g2000cwa.googlegroups.com>
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.