Subject: bug#24459: 24.5; cl-flet and cl-labels are not properly documented

From: Robert Cochran <[email-masked]>
Date: Mon, 19 Sep 2016 19:32:29 -0700

Carlos García <[email-masked]> writes:

>  Dear Maintainer,
>
>  I'd like to report a lack of documentation explaining the functions
>  cl-flet and cl-labels. The documentation does not specify how all
>  arguments are used and the exact purpose of the functions.
>
>  Regards,
>  Carlos
>

Here is the docstring for `cl-flet` (from my fairly recent git version):

  cl-flet is an autoloaded Lisp macro in ‘cl-macs.el’.

  (cl-flet ((FUNC ARGLIST BODY...) ...) FORM...)

  Make local function definitions.
  Like ‘cl-labels’ but the definitions are not recursive.
  Each binding can take the form (FUNC EXP) where
  FUNC is the function name, and EXP is an expression that returns the
  function value to which it should be bound, or it can take the more common
  form (FUNC ARGLIST BODY...) which is a shorthand
  for (FUNC (lambda ARGLIST BODY)).

I think that sounds pretty complete to me. I'll admit, I'm biased
because I know what flet is supposed to do, but there's nothing
noticably missing from that description.

Here is the docstring for `cl-labels` (from this same version):

  cl-labels is an autoloaded Lisp macro in ‘cl-macs.el’.

  (cl-labels ((FUNC ARGLIST BODY...) ...) FORM...)

  Make temporary function bindings.
  The bindings can be recursive and the scoping is lexical, but capturing them
  in closures will only work if ‘lexical-binding’ is in use.

The only thing I've got to say about this is that it may be a good idea
to point back to `cl-flet` from the `cl-labels` docstring, because the
`cl-flet` docstring is more complete.

I suppose you could also argue that `func` could be changed to ensure
that it's clear it's supposed to be a name. Again, I know that is what
it's supposed to be, but that may not be clear to readers that don't
already know.

As for use cases, well, I don't think that *any* docstring explicitly
spells out the 'exact purpose' of the function, but I'll go ahead and
give a couple examples for you:

1. Reducing global namespace polution from helper functions

; sum is supposed to take the single argument and sum all numbers below it
; ie 5 -> (+ 1 2 3 4 5) -> 15

(defun sum-1 (arg acc)
  (if (zerop arg)
      acc
    (sum-1 (1- arg) (+ acc arg))))

(defun sum (arg)
  (sum-1 arg 0))

vs

(defun sum (arg)
  (cl-labels (sum-1 (arg acc)
		    (if (zerop arg)
			acc
		      (sum-1 (1- arg) (+ acc arg))))
    (sum-1 arg 0)))

Notice how this example uses `cl-labels` because it is a recursive
definition.

2. Creating helper functions that use function-internal values

(defun get-value (data-structure key)
  (cdr (assoc key data-structure)))

(defun compute-foo ()
  (let* ((data-structure (create-data-structure))
	 (bar-value (get-value data-structure 'bar)))
    ; blah blah blah
    ))

vs

(defun compute-foo ()
  (let ((data-structure (create-data-structure)))
    (cl-flet (get-value (key) (assoc key data-structure))
      (let ((bar-value (get-value 'bar)))
	; blah blah blah
	))))

Notice how in the second version of `compute-foo`, `get-value` is able
to directly use `data-structure` instead of having to pass that in as an
argument. I used `cl-flet` because the function definition wasn't
recursive, so I didn't need `cl-labels`.

The one other big thing to use these functions for is when a lambda for
a higher-order function gets long enough that its stylistically a bad
idea to write it inline, but you don't want to declare it globally, like
in the first example.

Anyways, tl;dr:

The docstrings are pretty good as they are IMO, but I am speaking as one
who is familiar with what they are supposed to do. My only nit is that
it is a probably a good idea to have the docstring of `cl-labels` point back
to `cl-flet`, just as `cl-flet` points to `cl-labels`.

HTH,
-- 
~Robert Cochran

GPG Fingerprint - E778 2DD4 FEA6 6A68 6F26  AD2D E5C3 EB36 4886 8871