A form like
`(when (not (string-empty-p arg))
,@body
(message arg))
is equivalent to something like this:
(append '(when (not (string-empty-p arg)))
body
((message arg))))
So your function can be rewritten like this:
(defun make-anonymous-func (&rest body)
(lambda (arg)
(append '(when (not (string-empty-p arg)))
body
`((message arg)))))
And I think it's now very clear why this can't work. More generally, backquote forms provide a concise way of describing ways of constructing s-expressions by filling in templates, which is useful in macros, since s-expressions represent Lisp source code and macros are essentially functions whose values are source code, ad well as in other places where you want to construct s-expressions from templates.
(One horrible approach to do what you want is eval which evaluates source code. There are many reasons why this is a very bad answer however, so I won't go into it here.)
The general way to do what you're after is to rely on lexical scope. Conveniently elisp now has lexical scope, so you actually can do this. The following two examples assume that lexical-binding is true.
First of all if you merely want to capture the value of some variable bindings then you can do this:
(defun make-anonymous-func (msg)
(lambda (arg)
(when (not (string-empty-p arg))
(message msg)
(message arg))))
The second approach is if you want the function to run some general code: the way to do this is to pass it a function to call:
(defun make-anonymous-func (f)
(lambda (arg)
(when (not (string-empty-p arg))
(funcall f arg)
(message arg))))
And now
(funcall (make-anonymous-func
(lambda (m)
(message "foo")
(sit-for 1)))
"x")
Will cause two messages to appear, but this time there will be a pause between them so you'll see the first.
A final possibility, if what you want to do is create a bunch of similar functions statically – at compile time or definition time rather than dynamically at runtime – is to use a macro.
(defmacro define-messaging-function (name args &rest forms)
;; elisp has no destructuring, so check
(unless (and (listp args) (= (length args) 1)
(symbolp (car args)))
(error "bad arguments"))
(let ((arg (car args)))
`(defun ,name (,arg)
(when (not (string-empty-p ,arg))
,@forms
(message ,arg)))))
And now
(define-messaging-function foo (x)
(message "foo"))
Defines foo as a function which, when called, will do what the function returned by your original foo did.