3

How can I define an alias within a script with let-binding? I tried

(let (b-s)
  (defalias 'b-s 'buffer-string))

But it doesn't work.

I figured out to let-bind the variable where aliases as stored in, but I've not been able to find it.

  • The following works fine for me: (let (b-s) (defalias 'b-s #'buffer-string) (b-s)). Hitting C-x C-e after the last closing paren returns the buffer content as string. Not for you? Or do you try to achive something idfferent? – theldoria Jul 1 '17 at 10:29
  • 2
    @theldoria Note that this will globally set the function cell of the symbol b-s. In other words, after evaluating the code in question, you will be able to call (b-s) outside of the let-block as well. – Basil Jul 1 '17 at 10:49
6

Definitions

This topic confuses me sometimes, so let's start with some definitions, namely those of let:

let is a special form in ‘C source code’.

(let VARLIST BODY...)

Bind variables according to VARLIST then eval BODY.
The value of the last form in BODY is returned.
Each element of VARLIST is a symbol (which is bound to nil)
or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value of VALUEFORM).
All the VALUEFORMs are evalled before any symbols are bound.

and defalias:

defalias is a built-in function in ‘C source code’.

(defalias SYMBOL DEFINITION &optional DOCSTRING)

Set SYMBOL’s function definition to DEFINITION.
Associates the function with the current load file, if any.
The optional third argument DOCSTRING specifies the documentation string
for SYMBOL; if it is omitted or nil, SYMBOL uses the documentation string
determined by DEFINITION.

Internally, this normally uses ‘fset’, but if SYMBOL has a
‘defalias-fset-function’ property, the associated value is used instead.

The return value is undefined.

The most relevant thing to note is that let locally sets the value cell of a symbol, whereas defalias globally sets its function cell.

Reading Material

To better understand the difference, I recommend reading the Elisp Manual sections on Symbols, Variables and Functions. Simply reading the introductory paragraphs of each respective section should paint a clearer image. Of particular relevance to your question are the subsections on Creating Symbols, Local Variables and Function Names. The separate node on Local Variables in Macros also touches on this topic.

Solution

What follows from the comparison of let and defalias above is that, in order for the latter to have a local effect, we need a local (uninterned) symbol, not just a local variable. The way to achieve this is the following:

(let ((b-s (make-symbol "b-s")))
  (defalias b-s #'buffer-string)
  (funcall b-s))

This locally sets the value cell of b-s to an uninterned symbol whose function cell is set to the named function buffer-string. Thus we need to access the value of b-s as a variable in order to indirectly invoke buffer-string. Simply evaluating (b-s) would not work (unless such a named function already existed), as we have not set the function cell of the symbol b-s.

Discussion

I have yet to come across a real case of defalias being used for local bindings, and I find it difficult to imagine its usefulness or necessity. Could you please elaborate on why you are pursuing this approach?

A far more common approach for local function bindings is directly assigning a function to a variable, e.g.

(let ((fmap (if accumulate #'mapcar #'mapc)))
  (funcall fmap fn list))

though such cases can often do away with the variable altogether:

(funcall (if accumulate #'mapcar #'mapc) fn list)

In truly more involved (read: imperative :) code, you might instead see the pattern

(let (send-fn)
  ;; ...lots of complicated things...
  (unless some-condition
    (setq send-fn #'message-send-mail-via-seven-proxies))
  ;; ...more checks...
  (and send-fn (funcall send-fn)))

Without further context, I cannot make further recommendations. I would, however, be very surprised if the code in question could not be rewritten to forgo using defalias.

Update

In a comment below, OP explains that the intention is to locally abbreviate function names in order to save on typing. For the reasons explained above, there is no pure Elisp means of achieving this, but luckily Emacs' Common Lisp extensions provide a solution. In Emacs 24.3 or newer:

(eval-when-compile
  (require 'cl-lib))

(cl-flet ((b-s #'buffer-string))
  (b-s))

This expands to something similar to the code I wrote before, except calls to (b-s) are additionally expanded to the equivalent of (funcall b-s). For more information on this feature, see (cl) Function Bindings.

See also this article by Artur Malabarba for a brief introduction to and discussion of related CL extensions. The article links to a SO question revolving around discrepancies between older and newer versions of the Emacs CL library, which are also described under (cl) Obsolete Macros. This comparison is expounded on by phils in this separate SO question.

In older versions of Emacs, you may instead have to write

(eval-when-compile
  (require 'cl))

(flet ((b-s () (buffer-string)))
  (b-s))

Someone will hopefully fix any incorrect usage of the CL library on my part (I have yet to familiarise myself with it).

  • Thank you. Let me explain. 1 - I work for a company that provide us a lot of tools in emacs-lisp (and I haven't red all the code of them). 2 - In my own script I do a massive use of some functions (e.g. buffer-string) so I figured out to abbreviate their names with aliases. 3 - I'm a let-binding paranoid. That said I was searching for a solution that works with a (b-s) call. Your needs (funcall b-s) so this invalidates the sense of the abbreviation. – Gabriele Nicolardi Jul 1 '17 at 11:21
  • 1
    @GabrieleNicolardi I've updated my answer to include a possible solution to this. – Basil Jul 1 '17 at 12:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.