The ret macro is simple, but useful. It allows you to bind a variable and simultaneously specify the final value of that variable as the result of the ret expression, hence the name “ret”, a blend of let and return. Here is the implementation of ret:
(defmacro ret (var val &body body) `(let ((,var ,val)) ,@body ,var))
And now for an example. If you want to write a function that converts a hash-table into an alist, here is how you could do that using ret.
(defun table->alist (table) (ret result '() (maphash (lambda (k v) (push (cons k v) result)) table)))
Personally, I find code using ret to be much clearer than the equivalent code using let. When writing the let version, I commonly find myself forgetting to return the value I want to at the end. Ret eliminates this problem by returning the result for me. The only downside I see to using ret is that it has the same problem as all of the “do” macros (dotimes, dolist, etc), they change where the resulting value comes from. Even so, this problem is much more mild with ret because ret will always change the control flow, whereas the “do” macros may or may not change the final value depending on the number of arguments passed to them.