Tuesday, October 2, 2012

updates to let-plus

I have just pushed some updates to the let-plus Github repository. The most important change is the handling of ignored values — let+ used to ignore nil, for example

(let+ (((a nil b) '(1 2 3)))   ; OLD SYNTAX, NO LONGER VALID

would ignore the second element of the list. The implementation of let+ would simply replace nil with gensyms in the AST and declare them ignored. This caused some unexpected problems, for example in

(let+ (((a nil &optional (c nil c?)) '(1 2 3))) ; OLD SYNTAX, NO LONGER VALID

nil is a value, not a placeholder for a variable. I could have extended let+ to work handle this, but that would have required destructuring the lambda lists internally instead of simply relying on destructuring-bind. Being lazy, I decided to change the syntax. From now on, users should use &ign to ignore variables. For example,

(let+ ((#(a &ign b) (vector 1 2 3)))
  (list a b)) ; => (1 3), no warning about the middle element not being used

The change is incompatible with the previous version (nil is no longer understood as being ignored and will most likely cause an error during macroexpansion), so you need to update your code if you are using let+. I am sorry about any inconvenience this may cause.

I also extended let+ to understand nesting in more places (when applicable, mostly for read-only forms). For example,

(let+ (((&slots-r/o ((&complex a b) bar))
        (make-instance 'foo :bar (complex 1 2))))
  (list 1 2)) ; => (1 2)

As usual, bug reports are welcome.


  1. I'm vehemently opposed to such an arbitrary and unnecessary abbreviation as &ign. Please make it &ignore or &ignored.

    I note that the lack of an easy way to parse lambda-lists has had an effect on your design. I really think it's urgent that we finally get a library for first-class lambda-list objects that would be easy to query and manipulate. That's yet another of my upcoming projects...

    Another thing: I don't really like the trend of anonymous ignored parameters, though I recognize that the traditional way of naming and then ignoring parameters is a bit verbose and annoying. So, you might consider a syntax that conveniently allows ignoring while still permitting assigning a name to the variable for explicitness/sanity/documentation purposes.

    What about something like this?

    (let+ ((#(a (&ignored second) b) (vector 1 2 3)))
    (list a b))

    1. Thanks for the suggestions, I will think about them.

  2. This comment has been removed by the author.

  3. Have you considered using underscore as the ignored parameter? I seem to remember it being used that way somewhere else. Alternatively, how about having certain symbol names being ignored? For example, any symbol ending in "=IGNORE" might be ignored. That way your example would be: ...(a b=ignore c) '(1 2 3)...

    1. I find the underscore too short, also, I didn't want conflicts with other libraries (not only symbol conflicts per se, but semantic confusion that would arise from recycling _ for various purposes). Also, I don't really like the idea of symbol names being parsed for information.

  4. This seems to break cl-random, which uses NIL.