Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Emacs Lisp Lambda Expressions Are Not Self-Evaluating (nullprogram.com)
78 points by signa11 on Feb 23, 2018 | hide | past | favorite | 12 comments


In Common Lisp:

a list (lambda () (print :foo)) is not code:

  CL-USER 10 > (let ((l '(lambda () (print :foo))))
                 (funcall l))
  
  Error: Argument to apply/funcall is not a function: (LAMBDA NIL (PRINT :FOO)).
But the unquoted lambda form is code, since Common Lisp has a LAMBDA macro

  CL-USER 13 > (let ((l (lambda () (print :foo))))
                 (funcall l))

  :FOO 
  :FOO
The LAMBDA macro transforms it into a FUNCTION special form with an embedded lambda expression:

  CL-USER 14 > (macroexpand-1 '(lambda () (print :foo)))
  (FUNCTION (LAMBDA NIL (PRINT :FOO)))
Thus in Common Lisp we can evaluate:

a) the special form FUNCTION:

  (FUNCTION (LAMBDA () (PRINT :FOO)))
b) the same, but using #' as a short version:

  #'(LAMBDA () (PRINT :FOO))
c) or the macro LAMBDA, which expands into a)

  (LAMBDA () (PRINT :FOO))


One other combination in Common Lisp is that lambda expressions are usable as the head of a form, in place of a function name:

  ;; Call a function object held in a variable
  (let ((add (lambda (a b) (+ a b))))
    (funcall add 1 2))
vs

  ;; Direct execution of a lambda expression
  ((lambda (a b) (+ a b)) 1 2)
Macro expansion can place a literal or generated lambda expression there instead of using FUNCALL at all.


> Macro expansion can place a literal or generated lambda expression there instead of using FUNCALL at all.

Not quite. This, for example, does not work:

(defmacro foo () '(lambda (x) x))

((foo) 1)

A macro can expand into a form whose CAR is a literal lambda expression, but because there is always an equivalent LET form I can't think of a circumstance where this can do anything but obfuscate the code.

Personally, I think this is a flaw in the design of CL. Long ago I proposed an extension that would allow the CAR of a form to be a macro:

http://www.flownet.com/ron/lisp/combination-hook.lisp

I think it's cool for pedagogy because it lets you do things like directly define the Y combinator in CL without a ton of FUNCALLs. But it never caught on.


That's not actually a limitation of emacs lisp. It's just a design decision.

I know exactly the spot in the code where they could do what you suggest, because I've often wondered why they don't.

elisp gets a bad rap. But it's mostly self-inflicted.

Also http://www.patrickkphillips.com/grammar/is-it-a-bad-rap-or-b...

A “rap sheet” is a criminal record, and a “bum rap” meant a false accusation (or even a false conviction). Even when the charge was valid, it should come as no surprise, a guilty party might claim it was a “bum rap” just to save face.

Had no idea what a bad rap actually meant.


It sure is lovely that a world has been created where most people don't even need to know what "rap sheet" means.


I don't know if this is worth mentioning, but a lambda expression datum can be coerce-d into a function:

  (let ((l '(lambda () (print :foo))))
    (funcall (coerce l 'function)))
not to mention passed to eval or compile.


Man, that digression into unexec reminds me of how ugly Emacs is under the covers. I do really appreciate having elisp available to creatively enhance my editor but... ugh.

So then comes the perennial reminder of Guile/Emacs. There'd be no unexec (in theory) and while this appears to be design-intent from a Common Lisp, maybe it could be cleaned up in bytecode. The last Guile/Emacs push was, I think, a Google SoC project with some community followup from someone who knows far more about Emacs and Lisp than I. Guile 2.2 is out and is really sweet (fast, feels modern, kind of exciting). I wonder if it's time for another push to completion? Has anyone gave the experimental branch a spin lately?


The useful thing about unexec is that it is general. I ported it from Emacs to GNU Make about ten years ago. I was then able to instantly run incremental make jobs (where the rule tree of included Makefiles was so large that it otherwise took 30 seconds between typing make and the command to rebuild your file being dispatched!) GNU Make doesn't use any special data structures; just malloced structs with arbitrary pointers that no garbage collector is informed about.


But is it? I don't actually think it's bad design. Having system images of a pre-loaded heap is pretty common in many systems and languages. How such systems are generated and loaded vary, but they always come with some tricky issues. This is especially common in lisp-like systems.

The whole lisp implementation is emacs is a bit crummy, but I don't get the excessive criticism against unexec. I consider lexical binding a much a much bigger oversight.


Few things are pretty "under the covers." In large, that is why we have cover panels, period. Have you looked under the panel of your computer lately? :)


What an interesting question! Many computers are an impenetrable "unibody" or otherwise glued-together cryptex these days :).

Guile/Emacs offers a way to clear a staggering amount of baggage, offering some great features, while mostly keeping add-on packages compatible. You'd think it would get more love. I use Emacs (having switch from Vim because of elisp) but I'm definitely not well positioned to do anything here :/. As it is, I'm a very strange Emacs user: no emacs-server and always '-nw' inside tmux.


Even the unibody ones are a mess, if you can get under the body. That is half the point of having the body, after all.

Not to say we shouldn't shake off some baggage. Just at some point having things "tidy" is purely an indulgence.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: