20

How do you return early from a function before it's ended? For example:

(defun my-func () 
 "for example."
 (unless something (return nil))
 ; continue as usual...
 (+ 42 1))
ocodo
  • 1,202
  • 11
  • 20

2 Answers2

26

We have a number of options available. In general, what you are looking for is a "nonlocal exit"; see (info "(elisp) Nonlocal Exits").

Throw

You can catch / throw to exit the function with a value. See (info "(elisp) Catch and Throw") for details.

example:

(defun my-func ()
  "Simplistic `catch'/`throw' example."
  (catch 'my-early-return
    (when t
      (throw 'my-early-return "this is the short-circuit result of catch"))
    "this is the fallback result of catch"))

Block

You can also use cl-block and cl-return-from (although you will need to require cl-lib). See (info "(cl) Blocks and Exits") for details.

example:

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

(defun my-func ()
  "Simplistic `cl-block'/`cl-return-from' example."
  (cl-block my-func
    (when t
      (cl-return-from my-func))
    (1+ 42)))

cl-defun

We also have cl-defun which has an implicit cl-block with the same name as the function, so we can use the cl-block style with less typing.

example:

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

(cl-defun my-func ()
  "Simplistic `cl-defun' implicit block example."
  (when t
    (cl-return-from my-func)) ; `my-func' is an implicit block.
  (+ 42 1))

defun*

In old Emacsen, specifically Emacs 24.2 and earlier, cl-defun was provided as defun* by the now-obsolete library cl.el.

example:

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

(defun* my-func ()
  "Simplistic `defun*' implicit block example."
  (when t
    (return-from my-func)) ; `my-func' is an implicit block.
  (+ 42 1))

Note, however, that in Emacsen that old you can instead install cl-lib as a compatibility layer from GNU[-devel] ELPA:

Basil
  • 12,019
  • 43
  • 69
ocodo
  • 1,202
  • 11
  • 20
  • 6
    Note that if you don't have a preference for the CL syntax, `catch`/`throw` is more idiomatic in elisp, as other approaches are ultimately implemented in terms of catch/throw. The elisp manual says: "Most other versions of Lisp, including Common Lisp, have several ways of transferring control nonsequentially: `return`, `return-from`, and `go`, for example. Emacs Lisp has only `throw`." – phils Jan 31 '15 at 05:28
8

In addition to what @EmacsFodder covered, just raise an error.

This will not help if the code is called within (dynamically, not lexically) the extent of error-handling constructs such as ignore-errors or condition-case, but otherwise it is a fine way to exit a function. It is in fact what is done most of the time.

(defun my-func () 
 "..."
 (unless something (error "Whoops!"))
 ; continue as usual...
 (+ 42 1))

If you want to handle the error yourself then you can put the calling code (e.g. the call to something that ulimately calls my-func) inside a condition-case. Again, this is what is done most of the time, at least as often as using catch + throw. It all depends on what behavior you want.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • Thanks for the answer Drew, I agree this is quite a common method. However, just doing an early return in a lot of other languages, doesn't incur the _complexity_ of having to then deal with an error. When researching the question/answer set. I was specifically looking for alternatives to the "erroring out" style which always feels kludgy to me. I didn't specify this explicitly in the question text mind you. – ocodo Feb 01 '15 at 01:00
  • 1
    It all depends on what you want to do. If you want to end immediately, with no further processing/handling, then raising an error is a good way to go for a nonlocal exit, in Emacs. If you want to do something during a nonlocal exit, i.e., handle it in some way, then `catch`, `unwind-protect`, `condition-case` and the like are useful. There is a whole section of the Elisp manual devoted to [nonlocal exits](http://www.gnu.org/software/emacs/manual/html_node/elisp/Nonlocal-Exits.html). (And there is nothing particularly kludgy about any of them, IMO.) – Drew Feb 01 '15 at 02:54
  • "Feels" is entirely subjective of course. Thanks for the nonlocal manual ref. – ocodo Feb 01 '15 at 02:56