3

I am a new emacs user. And trying to write some Elisp to learn this software better. While I meet some problems.

I want to use apply on progn to run a list of functions in sequence. But I got errors as below. I am just confused and need some help to tell me how to achieve this in Elisp, thanks.

(progn
  (+ 1 1)
  (+ 2 2)
  )
;; => 4

(apply #'progn ((+ 1 1) (+ 2 2)))
;; => Invalid function: (+ 1 1)

(apply #'progn ('(+ 1 1) '(+ 2 2)))
;; => Invalid function: '(+ 1 1)

(apply #'progn '((+ 1 1) (+ 2 2)))
;; => Invalid function: #<subr progn>

I toy play as above cause I come to emacs from vim. So I installed evil and evil number. And want back the vim number inc and dec bindings C-a, C-x. So I write a macro as blow to avoid boilerplate.

(defmacro set-evil-number-keymap (key-set func &rest modes)
  `(apply 'progn
         ,(-map
          (lambda (mode)
            `(define-key ,(intern (concat "evil-" mode "-state-map")) (kbd ,key-set)
              ',(intern
                 (concat "evil-numbers/" func))))
          ,modes)))

(set-evil-number-keymap "C-a" "inc-at-pt" "normal" "insert")
(set-evil-number-keymap "C-x" "dec-at-pt" "normal" "insert")

My expectation is something below. While it just not run.


(progn 
(define-key evil-normal-state-map (kbd "C-a") ’evil-numbers/inc-at-pt) (define-key evil-visual-state-map (kbd "C-a") ’evil-numbers/inc-at-pt))

(progn 
(define-key evil-normal-state-map (kbd "C-x") ’evil-numbers/dec-at-pt) (define-key evil-visual-state-map (kbd "C-x") ’evil-numbers/dec-at-pt))


Drew
  • 29,895
  • 7
  • 74
  • 104
Xiong chenyu
  • 135
  • 1
  • 9
  • What's the question now? Why `(apply 'progn ...)` doesn't work? – xuchunyang Nov 30 '19 at 15:07
  • @xuchunyang cause `progn` is "special form", I thought it's a function but it is not. when do `macroexpand` on the `(apply 'progn ...)` I will get `(apply 'progn (define-key evil-normal-state-map (kbd "C-a") ’evil-numbers/inc-at-pt) (define-key evil-visual-state-map (kbd "C-a") ’evil-numbers/inc-at-pt))` – Xiong chenyu Nov 30 '19 at 15:25
  • Still not sure what's your question. You need one since this's a QA site, not a forum. Since you're using macro (actually you don't have to, the easy-to-read/write plain defun can work fine), you don't need `apply`. The purpose of `apply` is that, for example, you have some numbers stored in `some-numbers` and want the sum, you use `(apply '+ some-numbers)`, but with macro, you can just write `(cons '+ some-numbers)`, no apply is needed, you can think that macro should return a list, that is, a funcall such as `(+ 1 2 3)`, later then it will be evaluated automatically. – xuchunyang Nov 30 '19 at 16:17
  • @xuchunyang Sorry for confusing you, the question can be clarified as to why can't `apply` on `progn`? I think `progn` is just a function. All "special form" should be just a function and can easily composite with others. Why elisp need the concept of "special form", make it distinguish from normal function, and prevent `apply` on it. – Xiong chenyu Nov 30 '19 at 16:40

4 Answers4

3

You can't call apply on "special forms" like progn (see info (elisp)Special Forms), which have unique evaluation rules.

progn itself already provides a mechanism for sequenced evaluation, so your initial attempt does do what you want. At the top-level, however, the forms will be executed sequentially anyway, so there is no need for a progn there.

Rorschach
  • 31,301
  • 5
  • 78
  • 129
  • I see, thanks. I thought everything related to symbol, value, function, and macro. Now, I need to dig more about "special forms". – Xiong chenyu Nov 30 '19 at 14:00
  • 1
    @Xiongchenyu I think that does about cover it -- except lisp symbols are represented as structs that can have both a value and function components -- see eg. `(symbol-function 'progn)`, `(symbol-value 'default-directory)`, so you could have `(defun f ())` and `(defvar f "f")` – Rorschach Nov 30 '19 at 14:57
2

You don't need to use apply in the macro, elisp have ,@ to flatten a list to args. So the correct answer is

(defmacro set-evil-number-keymap (key-set func &rest modes)
  `(progn
          ,@(-map
             (lambda (mode)
               `(define-key ,(intern (concat "evil-" mode "-state-map")) (kbd ,key-set)
                  ',(intern
                     (concat "evil-numbers/" func))))
             ,modes)))
Xiong chenyu
  • 135
  • 1
  • 9
0

Not sure what you're expecting. progn returns value of the last forms, so 4 is excepted, consult C-h f progn:

progn is a special form in `C source code'.

(progn BODY...)

Eval BODY forms sequentially and return value of last one.

If you want to see the result of (+ 1 1), you need to ask for it, for example,

(list (+ 1 1) (+ 2 2))
;; => (2 4)

;; Step 1
(list 2 (+ 2 2))

;; Step 2
(list 2 4)

;; Step 3
(2 4)

The function arguments are evaluated from left to right. By the way, Emacs Lisp doesn't support multiple return value.


(apply #'progn ((+ 1 1) (+ 2 2)))
;; => Invalid function: (+ 1 1)

Since the second argument of the function apply is ((+ 1 1) (+ 2 2)), Emacs needs to calculate its value and to calculate form like (foo 1 2 3), foo has to be a function, but (+ 1 2) is not a function, it's just an ordinary list with 3 elements.

(apply #'progn ('(+ 1 1) '(+ 2 2)))
;; => Invalid function: '(+ 1 1)

'(+ 1 2), that is, (quote (+ 1 2)) isn't a function either, it's a list with two elements.

(apply #'progn '((+ 1 1) (+ 2 2)))
;; => Invalid function: #<subr progn>

progn isn't a function, it's a special form. and, or, let and while are all special forms, they are like macros, they doesn't evaluate arguments eagerly. (elisp) Calling Functions mentions

special forms and macros do not make sense in `apply'.

xuchunyang
  • 919
  • 7
  • 12
0

You had a bit of an XY problem, but what works for your nominal question is to

(eval (cons 'progn
            '((print (+ 1 1))
              (print (+ 2 2)))))

eval is generally considered a beginner programmer's misunderstanding, but in this case you just can't use apply for the reasons Rorschach explained. Elisp actually follows the Common Lisp spec here, which says for apply that

it is illegal for the symbol to be the name of a macro or special form

So, in general, you have to use eval directly on the program you've assembled by cons-ing a progn to your "instruction list".

Fizz
  • 4,782
  • 1
  • 24
  • 51