Saturday, 5 January 2013

Syntax for Lambda-Lift/Positional-Lambda

I've checked out the clever Lambda-Lift library by Hexstream, and I like it. It's a library that provides a succinct way to write positional lambdas. Having recently used implemented a reader macro, I decided to try one out for it. Almost immediately afterwards, I read the Reddit announcement, which mentioned that not using a reader macro was a good idea.

It seems that wanting to use reader macros for small things is a common weakness in Common Lisp programmers. Uses of dispatching macro character pairs to shorten code can actually make the code harder to read. This is more likely for people who don't know what the associated dispatch function does (or even sometimes where it came from). The tendency towards reader macros may arise from the desire for a more concise way to write Lisp code. This may be more of an issue if you are used to writing very terse code, such as Perl of Mathematica.

The issue with reader macros for general consumption is that they provide another layer to understand. While definitely not insurmountable, effort is still required to investigate and understand what the reader macro does or produces. In effect, reader macros use syntax to compress Lisp code.

The most Lispy way to compress code is probably to use higher order functions and macros. I think the benefit of using macros is that building macros upon macros is easier because of the uniform syntax. When macros are the right choice, you can benefit from their rich support in Common Lisp.

Anyway, here's my attempt. Use or leave it.

(defun read-positional-lambda-form (stream char n)
    (declare (ignore char))
    (let* ((next (peek-char nil stream))
           (n (or n (and (digit-char-p next)
                         (- (char-code (read-char stream))

                            #.(char-code #\0)))))
           (next (peek-char nil stream))
           (rest (and (char-equal next #\r) next))
           (next (progn (when rest (read-char stream))

                        (peek-char nil stream)))
           (body (if (member next '(#\Space #\Tab #\Newline nil #\)))
                    '() `(,(read stream nil nil t)))))
        `(lambda-lift:lift ,@(when n `(,(intern (format nil "~D" n) '#:keyword)))
               ,@(when rest `(&rest ,(read-from-string (string rest))))
               ,@body)))

(set-dispatch-macro-character #\# #\L #'read-positional-lambda-form)


The above code is released into the Public Domain the same as the Lambda-Lift library. Here are some examples of it:

'#L3           ;== (lift :3)
'#L3(print :2) ;== (lift :3 (print :2))
'#L2r          ;== (lift :2 &rest r)
'#L3r(print r) ;== (lift :3 &rest r (print r))
'#L3:2         ;== (lift :3 :2)
'#L2rr         ;== (lift :2 &rest r r)
(funcall #L5  
         'one 'two 'three 'four 'five) ;== FIVE
(funcall #L5:3          'one 'two 'three 'four 'five) ;== THREE
(funcall #L5(string :1) 'one 'two 'three 'four 'five) ;== "ONE"
(funcall #L5(reverse (string :4)) 'one 'two 'three 'four 'five) ;== "RUOF"
(funcall #L2rr 'one 'two 'three 'four 'five) ;== (THREE FOUR FIVE)

No comments:

Post a comment