5

I'm creating a modular system for my use of Emacs, so I can call modules on the fly, as I need them. Just like a lazy call, but I must explicitly call them.

Anyways, I found this riddle, I want a function lw/define-loadable such that

(defun lw/define-loadable (fn-name mod-name)
  (defun fn-name ()
    (interactive)
    (load! (concat "../modules/" mod-name))))

when I call

(lw/define-loadable "lw/load-chinese" "chinese.el")

Will have the same effect of calling

(defun lw/load-chinese ()
  (interactive)
  (load! "../modules/chinese.el"))

Currently, the call will define the funtion fn-name as it were hard-coded.

2 Answers 2

5

I would use a macro, it is easier and this is what macros are for:

(defmacro lw/define-loadable (fn-name mod-name)
  `(defun ,fn-name ()
    (interactive)
    (print (concat "../modules/" ,mod-name))))

;; Example call:
(lw/define-loadable my "you")
(my)

;; "../modules/you"

But if you insist on using a function, this will do:

(defun lw/define-loadable-2 (fn-name mod-name)
  (fset fn-name
        (lambda ()
          (interactive)
          (print (concat "../modules/" mod-name)))))

;; Example call
(lw/define-loadable-2 'my2 "you")
(my2)
;; "../modules/you"

This function creates a closure for every command you bind.

Note, that you have to use a symbol as argument in order to generate a valid function binding. If you need the argument to be a string, then use (make-symbol fn-name) inside the macro or function.

2
  • Perfect, thank you. Can I follow you on GitHub? I haven't found any links in your profile.
    – BuddhiLW
    Commented Feb 11, 2022 at 15:41
  • I try to keep logins for various services separated ...
    – jue
    Commented Feb 11, 2022 at 17:07
2

I too would use an Elisp macro, such as @jue's answer shows. But I'll mention that you could also do this:

(defun lw/define-loadable (fn-name mod-name)
  (fset fn-name `(lambda ()
                  (interactive)
                  (load! (concat "../modules/" ,mod-name)))))

(lw/define-loadable-2 'my2 "you")

(symbol-function 'my2)
;; ->
;; (lambda nil (interactive) (load! (concat "../modules/" "you")))

fset is a function; it evaluates its arguments. The backquoted lambda sexp constructs the function definition (as a list with car lambda) similarly to what a macro does.

You could also do the concatenation at function-definition time:

(defun lw/define-loadable (fn-name mod-name)
  (fset fn-name `(lambda ()
                  (interactive)
                  (load! ,(concat "../modules/" mod-name)))))

(lw/define-loadable-2 'my2 "you")

(symbol-function 'my2)
;; ->
;; (lambda nil (interactive) (load! "../modules/you"))
;;                                  ^^^^^^^^^^^^^^^^

Finally, you can also use defalias in place of fset. See C-h f defalias for the difference.

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.