Discussion on possible lambda syntax: Good or Evil?

Discussion of Common Lisp
PhazeDK
Posts: 3
Joined: Mon Sep 22, 2008 7:12 am
Location: Denmark

Discussion on possible lambda syntax: Good or Evil?

Post by PhazeDK » Mon Sep 22, 2008 10:41 am

One of the few things I've been coding in CL is a shorthand form of the dearly loved lambda functions. While my own code proved to work only in CLISP, and not entirely as well as I had hoped, I thought I'd share my experience and ideas about this whole thing.

The idea came from either a blog or comp.lang.lisp, I don't remember. Here's how it looks:

Code: Select all

{and $1 $2} expands to (lambda ($1 $2) (and $1 $2))
Other versions of this syntax have been discussed. For example, sbcl supports unicode, and the lambda character could be used instead of $:

Code: Select all

#$(and $1 $2)
Using brackets and a dispatch character.

λ(and λ1 λ2)
Using the lambda character.
I like the first suggestion the best since I can do pretty function calls:

Code: Select all

#'{and $1 $2}
The lambda character is pretty awesome, albeit cumbersome.

This would support nested lambda calls by adding multiple $'s before the argument count. $1 would be a 'first-layer' argument, $$1 a 'second-layer', $$3 a third, and so on. Let me show you:

Code: Select all

{let ((count $1)) {incf count $2}}
Returns a pre-initialized lambda closure depending on the arguments passed. (Note, $2 is an argument to the FIRST lambda call, not the second.)

{if $1 {+ $2 $$1} {* $2 $$1}}
Returns a lambda function that either multiplies or adds by a set amount, depending on the arguments passed.
This can be done for any amount of nested lambda calls. Each lambda function will then have it's own unique set of arguments.

It supports &rest arguments by using $rest.

Code: Select all

{* $1 (apply #'+ $rest)} expands to (lambda ($1 &rest $rest) (* $1 (apply #'+ $rest)))
Multiplies the first argument by the sum of the remaining arguments.
$rest is also supported in nesting. $$rest would be a second layer lambda &rest argument etc.

Arguments are sorted by using #'< (with $rest being weighted to the end). Any numbers can theoretically be used.

Code: Select all

{/ (- $234931) $0}
Divides the negative of the second argument by the first.
All these 'features' can be mixed and matched.

My own version gensymmed the names of the arguments, and this is probably necessary for it to work.

My question: Is this Good, or is this Evil? I like it. It keeps my code short. I don't have to come up with redundant names for my arguments (maybe we could even find a way so i don't have to come up with names for the variables in my closures? Or maybe not.). This makes it a valid abstraction. Finally, IMHO, It looks good.

Really, I just don't see why I have to name my arguments when I don't name my function.

What do you think? Good or Evil?

ramarren
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland
Contact:

Re: Discussion on possible lambda syntax: Good or Evil?

Post by ramarren » Mon Sep 22, 2008 11:22 am

Well, I wouldn't call it Evil, but not exactly close to Good, either. Personally, I prefer more functional approach of using compose/curry/rcurry functions, from, say Alexandria, misnamed as they are. They allow me to avoid explicit lambda in simple cases, and for more complex case I think it is better to give a function a name, if only for documentation purposes. If one does not want to pollute the global namespace there are always flet/labels.

Admittedly the functional approach doesn't work on macros, but I think doing that is usually a sign that something went wrong. For example functional equivalent of (and ...) is (every #'identity ...), but you probably know that. If ever, this comes rarely enough that explicit (lambda ...) doesn't hurt, anyway.

danb
Posts: 35
Joined: Sat Jun 28, 2008 1:05 pm
Location: Urbana, Illinois, US
Contact:

KISS

Post by danb » Mon Sep 22, 2008 7:57 pm

PhazeDK wrote:

Code: Select all

{and $1 $2} 
#$(and $1 $2)
I use a simpler but less powerful system called #func.
It's more like partial application in ML:

Code: Select all

#f(push _ xs) => (lambda (#:G1) (push #:G1 xs))
#f[_ + 1]     => (lambda (#:G2) (+ #:G2 1))
#3f(foo) => (lambda (#:G3 #:G4 #:G5) (foo #:G3 #:G4 #:G5))
Goo has something similar in its OP macro, although I prefer a reader macro, and OP uses "..." for a rest parameter.
This would support nested lambda calls by adding multiple $'s before the argument count.

Code: Select all

{if $1 {+ $2 $$1} {* $2 $$1}}
This means you may have to add or remove dollar signs if you want to move lambda expressions around in your code.
My question: Is this Good, or is this Evil?
It's powerful but complex. How often would it be used?
I like it.
Everybody likes their own code ;)
Writing complex libraries is fun, and it's a good learning experience too, but I think it's especially important to emphasize simplicity in language and library design. If I'm going to write a large or complex lamba function, I don't mind using LAMBDA, because it's helpful for a large chunk of code to have a large deliimiter.
Finally, IMHO, It looks good.
It looks Perlish. Perlishness is a well-trodden subject, so I'll leave it at that :D
I just don't see why I have to name my arguments when I don't name my function.
Documentation. You call any operators inside the function by name, so it's not necessarily so bad for the arguments to have names.

Kompottkin
Posts: 94
Joined: Mon Jul 21, 2008 7:26 am
Location: München, Germany
Contact:

Re: KISS

Post by Kompottkin » Tue Sep 23, 2008 12:08 am

danb wrote:
I like it.
Everybody likes their own code ;)
No. Definitely not.

In fact, I wrote something similar to this a while ago -- something which I've found ugly as hell right from the first release. Its usage originally looked like this:

Code: Select all

(fn + _0 _1 3)
(fn * _ 3)
(fn funcall (fn + _ 3) _1)  ; a function that adds 3 to its second argument
This had a number of problems: _0, _1 and so on are potential numbers, so the code isn't CLHS-portable (it seems to be portable across all CL implementations I've tried, however); there is no implicit progn, and you can't easily write what would be (lambda (x y z) z) or even (lambda (&rest args) args); and there is no syntactic hint that the + in (fn +) actually means the value in the function namespace, which is simply weird.

Trying to tackle some of these problems, I changed the syntax like this:

Code: Select all

(fn #'+ _0 _1 3)   ;or: (fn (+ _0 _1 3))
(fn #'* _ 3)  ;or: (fn (* _ 3))
(fn #'funcall (fn + _ 3) _1)
(fn () _1)   ;here the body is an implicit progn because the first body form is a list
at which point I couldn't decide whether the new version looked less ugly or even uglier than the old one already.

There were even distinct macros with argument-number checking and without (FN was the one without):

Code: Select all

(funcall (fn () _3) 1 2 3 4 5)          ;=> 4 
(funcall (efn () _3) 1 2 3 4 5)         ;=> error 
Finally, I also added a currying version, arguably the most misguided attempt at Lisp syntax “encancement” ever made:

Code: Select all

(funcall (fn* #'+ 1) 2)               ;=> 3 
(funcall (fn* #'+) 1 2)               ;=> 3 
(funcall (fn* (/ (* _ 4))) 3 6)       ;=> 2 
I can't even read the last line without despairing completely! I've copied it from the docs I wrote at that time. Well, at least I wrote docs.

I never used the code. Not even once.

findinglisp
Posts: 447
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX
Contact:

Re: KISS

Post by findinglisp » Tue Sep 23, 2008 7:40 am

Kompottkin wrote:I never used the code. Not even once.
Sometimes you just need to go through the exercise to get it out of your system. ;) Everybody throws away a lot of code. No biggie.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

danb
Posts: 35
Joined: Sat Jun 28, 2008 1:05 pm
Location: Urbana, Illinois, US
Contact:

Re: Fanboyism

Post by danb » Tue Sep 23, 2008 10:22 am

Kompottkin wrote:
danb wrote:Everybody likes their own code ;)
No. Definitely not.
So I exaggerated :)
Anyway, in most cases, if you don't like your own code, you don't have to use it. So it's common to see people touting their own favorite systems, which may or may not appeal to others.

PhazeDK
Posts: 3
Joined: Mon Sep 22, 2008 7:12 am
Location: Denmark

Re: Discussion on possible lambda syntax: Good or Evil?

Post by PhazeDK » Tue Sep 23, 2008 12:50 pm

Ramarren wrote:Personally, I prefer more functional approach of using compose/curry/rcurry functions, from, say Alexandria, misnamed as they are.
Evidently, CL allows many ways to solve the same problem, and this does not really point to whether the syntax is readable or not and whether it is a usable abstraction or not. True, lambda's aren't used as much as my post may have lead to believe. I don't see why that should prohibit new syntax.
danb wrote: I use a simpler but less powerful system called #func.
It's more like partial application in ML:

Code: Select all

#f(push _ xs) => (lambda (#:G1) (push #:G1 xs))
#f[_ + 1]     => (lambda (#:G2) (+ #:G2 1))
#3f(foo) => (lambda (#:G3 #:G4 #:G5) (foo #:G3 #:G4 #:G5))
Personally, I find this syntax confusing. Of course, I haven't worked with--let alone coded--that, and I have no experience of ML. :P
Then again, a very elegant use of the dispatch reader macro.
danb wrote:
This would support nested lambda calls by adding multiple $'s before the argument count.

Code: Select all

{if $1 {+ $2 $$1} {* $2 $$1}}
This means you may have to add or remove dollar signs if you want to move lambda expressions around in your code.
Right, and this makes it worse than a regular lambda call how? Normally, I would have to add all my arguments to the lambda list when cleaning up nested lambda calls.
danb wrote:
My question: Is this Good, or is this Evil?
It's powerful but complex. How often would it be used?
To me it's neither powerful nor complex. True, perhaps a bit more complex than it is powerful, i still find it a nice abstraction. It's simple, in the way that it does a single, simple job of removing the lambda list. It's weak in that it provides no new features or data abstractions. It's only benefit is when writing and reading. (More so writing than reading? Perhaps only to be used in the REPL?)
danb wrote: It looks Perlish. Perlishness is a well-trodden subject, so I'll leave it at that :D
It looks Perlish because it uses dollarsigns and braces. To me, it feels and works very LISPish, since it would be implemented with reader macros. That is, there is still an underlying function, which can be used regardless.
danb wrote:
I just don't see why I have to name my arguments when I don't name my function.
Documentation. You call any operators inside the function by name, so it's not necessarily so bad for the arguments to have names.
In the case where documentation is needed, I would use a defined function. But in the cases where I am using a lambda expression, arguments usually don't make it more clear, as the lambda function will always be in context. If it's confusing what data the lambda is actually working on, well then maybe a lambda expression isn't the answer.
Kompottkin wrote: Finally, I also added a currying version, arguably the most misguided attempt at Lisp syntax “encancement” ever made.
And that's what I wan't to know. Is this the second most misguided attempt at Lisp syntax enhancement ever made?

danb
Posts: 35
Joined: Sat Jun 28, 2008 1:05 pm
Location: Urbana, Illinois, US
Contact:

Re: Discussion on possible lambda syntax: Good or Evil?

Post by danb » Tue Sep 23, 2008 10:39 pm

Code: Select all

#f(push _ xs) => (lambda (#:G1) (push #:G1 xs))
#f[_ + 1]     => (lambda (#:G2) (+ #:G2 1))
#3f(foo) => (lambda (#:G3 #:G4 #:G5) (foo #:G3 #:G4 #:G5))
PhazeDK wrote:Personally, I find this syntax confusing.
What's confusing about it? Each underline represents a distinct argument, and the number between the # and the F is the number of extra trailing arguments. The square brackets are a separate reader macro for infix syntax. I originally used curly braces for lambdas, but it's much easier to combine #F with the infix brackets.
I have no experience of ML. :P
There's a ton of good stuff in the ML languages. #func is as close as I could get to the syntax of partial application.
Then again, a very elegant use of the dispatch reader macro.
I guess the #Nx notation is pretty standard. Just another great feature of CL :P
danb wrote:

Code: Select all

{if $1 {+ $2 $$1} {* $2 $$1}}
This means you may have to add or remove dollar signs if you want to move lambda expressions around in your code.
PhazeDK wrote:Right, and this makes it worse than a regular lambda call how? Normally, I would have to add all my arguments to the lambda list when cleaning up nested lambda calls.
What do you mean "cleaning up"? I was thinking about when you change the nesting level of a lambda, you have to change the number of dollar signs. You don't have to do that with regular LAMBDAs.
danb wrote: It looks Perlish. Perlishness is a well-trodden subject, so I'll leave it at that :D
PhazeDK wrote:It looks Perlish because it uses dollarsigns and braces.
Exactly :D
To me, it feels and works very LISPish, since it would be implemented with reader macros. That is, there is still an underlying function, which can be used regardless.
I thought you were just referring to the visual appearance. I'm sure it works fine.
Is this the second most misguided attempt at Lisp syntax enhancement ever made?
I would say it's okay if it works for you. I worry about complex reader macros making it harder to write regular macros, but the ultimate test of your system is how convenient you find it.

sburson
Posts: 6
Joined: Wed Sep 24, 2008 5:16 pm

Re: Discussion on possible lambda syntax: Good or Evil?

Post by sburson » Wed Sep 24, 2008 5:46 pm

There is no such thing as a "lambda function". The term is ill-formed because it confuses the syntactic domain (the domain of source code) with the semantic domain (the domain of values and objects manipulated by the program). Correct terminology refers to lambda expressions in the syntactic domain; in the semantic domain, there are simply functions -- it does not matter whether they were created by evaluating a lambda expression, or by evaluating a form like #'FOO (indeed, CL provides no portable way to tell the difference).

That aside, let me answer your question. Personally, I don't care for this way of abbreviating lambda expressions. If I could go back in time and alter the ANSI CL spec, I would make two changes to make lambda expressions easier to write: I would allow FN as a synonym of LAMBDA and deprecate the latter, and I would adopt the Zetalisp rule that parameters named IGNORE are automatically ignored, so you don't have to write a declaration to ignore them. Well, actually, instead of IGNORE, I would be tempted to borrow a Prolog convention and use _ (a single underscore).

So, for instance, instead of

Code: Select all

(lambda (x y z)
  (declare (ignore y))
  (+ x z))
you could simply write

Code: Select all

(fn (x _ z) (+ x z))
This would be enough improvement for me, and doesn't run into the difficulties that these implicit-parameter notations do, with the need for multiple $ signs etc.

One could certainly write a macro FN that did this, but I haven't bothered because DEFUN, DEFMETHOD, LABELS, etc. wouldn't thereby accept the _ syntax, and also it wouldn't work to say #'(fn ...), so it would only be a partial solution.

S11001001
Posts: 5
Joined: Tue Sep 30, 2008 9:18 pm
Location: Indiana, USA
Contact:

Re: Discussion on possible lambda syntax: Good or Evil?

Post by S11001001 » Tue Sep 30, 2008 9:37 pm

Arnesi exports fun as an alias of lambda, and also provides the #L syntax, where !1 etc are arguments.

Because lambdas are fun, you see.

Post Reply