Subject: Re: bug#58278: Add new function seq-keep

From: Jonas Bernoulli <[email-masked]>
Date: Tue, 04 Oct 2022 14:50:28 +0200

Lars Ingebrigtsen <[email-masked]> writes:

> Would a function signature like
>
> (cl-defgeneric seq-keep (function sequence &optional pred)
>   ...)
>
> make more sense for this combination of map/filter?  (The default
> predicate would, of course, be "not null".)

Yes, that would be an improvement.  Some (but certainly not all)
uses of dash's `-keep' look something like

  (-keep (lambda (elt)
           (and (symbolp elt)
                (symbol-name elt)))
         sequence)

and

  (seq-keep #'symbol-name sequence #'symbolp)

would be much nicer in those situations.

In at least some of those cases `mapcan' would work just as well
as `-keep', so after adding &optional PRED, `seq-keep' could also
be used in many places one would have reached for `mapcan' before,
making it even more useful.

I am unsure how I feel about it myself, but we should also consider

  (function pred sequence)

PRED wouldn't be optional then, but we should of course allow it to
be nil.  (Forcing the use of #'identity would not make sense, since
we want FUNCTION to serve both as a predicate and a transforming
function in that case.)

Concerning the argument order, in my opinion

  (seq-keep (lambda (elt)
              ...)
            (lambda (elt)
              ...)
            sequence)

looks better than

  (seq-keep (lambda (elt)
              ...)
            sequence
            (lambda (elt)
              ...))

because the variable named "sequence" looks out of place in the second
instance.  But of course all of FUNCTION, PRED and SEQUENCE can be just
a symbol or a more complex expression.  For instance if only PRED is
complex, then

  (seq-keep #'symbol-name sequence
            (lambda (elt)
               ...))

looks better than

  (seq-keep #'symbol-name
            (lambda (elt)
               ...)
            sequence)

So there is no order that is always best, from an aesthetic point
of view.  In my own use at least, most of the time either all three
arguments would be moderately complex expressions or only the two
functions arguments would complex and the sequence argument would be
just a variable. For that reason I would favor the two functions to
be placed next to each other, I think.

The (function pred sequence) argument list has the advantage that it
is in (reverse) "chronological" order.  First we have a sequence, then
we filter the elements, and finally we transform the elements that we
kept, at least conceptually.

Using actual chronological order (sequence pred function) is out of
question as that would conflict with all(?) other mapping functions;
as is (sequence [pred] function), I presume.

Just some food for thought; as of yet, I am unsure what order I
prefer myself -- though I lean towards (function pred sequence).
But if that is deemed unusual and thus undesirable, (function
sequence &optional pred) also works for me.