3

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.

BuddhiLW
  • 257
  • 1
  • 7

2 Answers2

4

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.

jue
  • 4,476
  • 8
  • 20
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.

Drew
  • 75,699
  • 9
  • 109
  • 225