4

I'd rather not use cl-lib and cl at the same time. However, I really like using lexical-let to specify the usage of lexical binding on a more granular level.

Is there any equivalent in cl-lib or vanilla Emacs Lisp?

3
  • It took me several hours, but I now have my own CL library with different names. You may wish to create your own at some point when you have some free time. My main motivation was keeping all custom libraries fully functional while being able to try out the latest Emacs Trunk with less likelihood of revisions by the Emacs team breaking my daily workflow.
    – lawlist
    Aug 31, 2015 at 3:47
  • One alternative is to create a global variable and set the value, which can be seen from within a nested lambda. Other alternatives, include feeding the nested lambda the variable from a previous setting, if it is known. I haven't tried a buffer-local variable, but that may work too -- i.e., (with-current-buffer xyz my-local-variable) -- i.e., get the value from within the nested lambda and use it.
    – lawlist
    Aug 31, 2015 at 3:57
  • I don't know enough CL to be sure, but my impression from the lexical-let docstring is that it works the same way as CL's let, and therefore it may be a deficiency of cl-lib (or rather cl-macs) that it doesn't implement cl-let as an alternative to lexical-let?
    – phils
    Aug 31, 2015 at 5:32

3 Answers 3

5

cl and cl-lib are not "either or". The former requires the latter and defines lexical-let.

If you want lexical bindings, you can turn them on using the variable lexical-binding.

You can also set it on a per-file basis using file variables.

9
  • I want to specify lexical binding for a single specific binding, not all the bindings in the file. How can I do that?
    – PythonNut
    Aug 31, 2015 at 3:16
  • I don't think you can do that without cl. Why would you want to?!
    – sds
    Aug 31, 2015 at 3:19
  • mainly because it's simpler and features less side-effects.
    – PythonNut
    Aug 31, 2015 at 3:21
  • @PythonNut I think the question is not why you want to use it there, but why do you not want to use it elsewhere in your file ?
    – YoungFrog
    Aug 31, 2015 at 12:26
  • @YoungFrog I just don't like enabling things when they aren't needed. It's the same reason I don't use setf instead of setq, even though it would work.
    – PythonNut
    Sep 5, 2015 at 16:10
5

Simply require library cl at compile time, to get the use of its macros (and not get any runtime load). That is where macro lexical-let is defined.

So all you need is this, to use lexical-let:

(eval-when-compile (require 'cl)) ;; lexical-let

(I put the stuff I use from the library in a comment like that, just to let me know what I'm using from it.)

Oh, and lexical-let works in all Emacs versions (at least Emacs 20 and later). And yes, it lets you use dynamic binding by default and use lexical binding in a granular way, when you want it.

(cl.el is vanilla Emacs, BTW, not a 3rd-party library. It is simply not preloaded. It has the same status as something like Org mode or Calc or dired-x.el.)

0

You can make precisely scoped lexical bindings of specific variables with eval - it has an optional second parameter for lexical bindings.

For example, if you evaluate

(eval
    '(lambda (x)
         (+ x a))
    '((a . 1)))

the result is (closure ((a . 1)) (x) (+ x a)). (Note that the (closure ...) representation is just an implementation detail, it's not guaranteed to stay the same between Emacs versions, and there's no closure function, macro, or special form.)


Of course, an eval with lexical bindings is harder to write and worse to read than lexical-let. And why write all of (a . a) just to say you want to capture the value of a? So personally I ended up writing a little macro for myself to make this nicer:

(defmacro lambda-let (varlist args &rest body)
    (declare (indent 2))
    (let (bindings parameter argument)
        (dolist (var varlist)
            (if (listp var)
                (if (length= var 2)
                    (setq parameter (car var)
                          argument  (cadr var))
                    (if (length= var 1)
                        (setq parameter (setq argument (car var)))
                        (eval `(let (,var)))))
                (setq parameter (setq argument var)))
            (push `(cons ',parameter ,argument) bindings))
        (if bindings
            (setq bindings (cons 'list (nreverse bindings)))
            (setq bindings t))
        `(eval
            '(lambda ,args
                ,@body)
            ,bindings)))

With that, the above (eval ...) can be replaced with just

(lambda-let ((a 1)) (x)
    (+ x a))

You get all the syntactical bells and whistles of a let form for the varlist, in a way that makes sense for lexical closures:

  1. (lambda-let (a) (x y) ...) lexically captures the current value of a.

  2. (lambda-let ((a)) (x y) ...) also does the same thing if you prefer that style.

  3. (lambda-let ((a 1)) (x y) ...) lexically captures 1 as the value of a.

  4. Of course you can capture multiple variables. For example, (lambda-let (a (b 2) c (d 4)) (x y) ...) captures the current value of a, captures 2 as the value of b, the current value of c, and 4 as the value of d.

  5. It even tries to ensure you get native let errors if you get the syntax wrong. For example, (lambda-let ((a 1 2)) () ...) will signal

    (error "`let' bindings can have only one value-form" a 1 2)
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.