emacselisp

Can I pass functions as arguments, in emacs lisp


I have many handy functions which operate on the current word or region, and due to laziness etc. I've build them out of a template...

For example

(defun lower-camelcase-at-point-or-region ()
  "lowerCamelCaseTheCurrent dashed or snake_case word or any words in text selection."
  (interactive)
  (let (pos1 pos2 meat)
    (if (and transient-mark-mode mark-active)
        (setq pos1 (region-beginning)
              pos2 (region-end))
      (setq pos1 (car (bounds-of-thing-at-point 'symbol))
            pos2 (cdr (bounds-of-thing-at-point 'symbol))))
    (setq meat (s-lower-camel-case (buffer-substring-no-properties pos1 pos2)))
    (delete-region pos1 pos2)
    (insert  meat)))

Effectively this is all boiler plate, except for this line...

(setq meat (s-lower-camel-case (buffer-substring-no-properties pos1 pos2)))

Where I call the function s-lower-camel-case on the buffer substring. I want to reuse the at point or region stuff, but without duplicating it everywhere, (because it's a headache to maintain.)

So what I really want to know is, can I pass functions as arguments in Emacs Lisp?

When I tried this...

(defun do-stuff-on-point-or-region ()
  "Do stuff."
  (interactive)
  (operate-on-point-or-region 's-lower-camel-case))

With operate-on-point-or-region defined as...:

(defun operate-on-point-or-region (fn)
  "Pick the substring at point, or region 
   and replace it with the output of fn"
  (let (pos1 pos2 meat)
    (if (and transient-mark-mode mark-active)
        (setq pos1 (region-beginning)
              pos2 (region-end))
      (setq pos1 (car (bounds-of-thing-at-point 'symbol))
            pos2 (cdr (bounds-of-thing-at-point 'symbol))))
    (setq meat (fn (buffer-substring-no-properties pos1 pos2)))
    (delete-region pos1 pos2)
    (insert  meat)))

I get : Symbol's function definition is void: fn

Is it possible to use a function as an argument fn in Emacs Lisp!? Or am I just doing it wrong?


UPDATE: The solution is to use funcall e.g.

(defun replace-thing-at-point-with (fn)
  "Get the current thing at point.
Replace with the return value of the function FN."
  (let* ((pos1 (car (bounds-of-thing-at-point 'symbol)))
         (pos2 (cdr (bounds-of-thing-at-point 'symbol)))
         replacement
         excerpt)
    (when (> pos1 0)
      (setq pos1 (- pos1 1)))
    (setq excerpt (buffer-substring-no-properties pos1 pos2))
    (setq replacement (funcall fn excerpt)) ;; <- here.
    (delete-region pos1 pos2)
    (insert replacement)))

Solution

  • First, emacs-lisp is, kinda, sorta, a 2-lisp, so the following is invalid:

    (defun foo (fn)
      (fn 3)) ;; DOES NOT WORK!
    

    Instead, you have to do the following:

    (defun foo (fn)
      (funcall fn 3))
    

    So if you replace (setq meat (fn with (setq meat (funcall fn the code ought to work.