[Review] SE-0118: Closure Parameter Names and Labels


(Chris Lattner) #1

Hello Swift community,

The review of "SE-0118: Closure Parameter Names and Labels" begins now and runs through July 11. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and contribute to the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?
  * Is the problem being addressed significant enough to warrant a change to Swift?
  * Does this proposal fit well with the feel and direction of Swift?
  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager


(Vladimir) #2

Hello Swift community,

The review of "SE-0118: Closure Parameter Names and Labels" begins now and runs through July 11. The proposal is available here:

> ..

  * What is your evaluation of the proposal?

Strong +1, more compact labels with clear meaning

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes

  * Does this proposal fit well with the feel and direction of Swift?

Yes

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read proposal, read discussion in list

···

On 06.07.2016 2:10, Chris Lattner via swift-evolution wrote:

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Rönnqvist) #3

   * What is your evaluation of the proposal?

+1. These look like good improvements to me. (I'm also happy that the map/filter/reduce naming was left out of this proposal)

One thing I was wonder about was the capitalization of UTF8 in the first example. Shouldn't that be s.withUTF8Buffer(processBytes)instead of s.withUtf8Buffer(processBytes) or am I confusing Swift's conventions with Cocoa's?

   * Is the problem being addressed significant enough to warrant a change to Swift?

Yes. Inconsistency at this point in Swifts evolution should be dealt with.

   * Does this proposal fit well with the feel and direction of Swift?

Yes. These proposed labels help the call site read better as a sentence.

   * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Not applicable

   * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading of the proposal. Followed some of the discussion.

- David

···

On 6 Jul 2016, at 01:10, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0118: Closure Parameter Names and Labels" begins now and runs through July 11. The proposal is available here:

   https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

   https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and contribute to the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

   * What is your evaluation of the proposal?
   * Is the problem being addressed significant enough to warrant a change to Swift?
   * Does this proposal fit well with the feel and direction of Swift?
   * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
   * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

   https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Erica Sadun) #4

I like the overall thoughtful and generally succinct results. I participated in the renaming thread and believe this will improve the affected APIs.

My one concern lies with "ManagedBuffer<Header,Element>.create( minimumCapacity: 10, makingValueWith: makeHeader)", which should either be "makingHeaderWith" or (better yet) "with".

-- E

···

On Jul 5, 2016, at 5:10 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0118: Closure Parameter Names and Labels" begins now and runs through July 11. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md

  * What is your evaluation of the proposal?
  * Is the problem being addressed significant enough to warrant a change to Swift?
  * Does this proposal fit well with the feel and direction of Swift?
  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?


(Jordan Rose) #5

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md ]

Hi, Dave, Dmitri, Max. Sorry I didn’t make the pre-review commentary thread. I find I’m still not happy with several of these names, although there are many other improvements. Stepping back, I think the Swift API guidelines just don’t do a good job with closure parameters. We should have naming guidelines for strategy closures and for callbacks.

lines.split(whereSeparator: isAllWhitespace)

This reads like an English sentence, but it doesn’t have the correct meaning for me. This implies a structure that has a pre-existing “separator", and checks if that separator matches the predicate, rather than searching for an element that matches the predicate, and splitting on that. I realize that the former reading doesn’t make much sense as a function, but it’s still impeded my understanding more than helping it along.

Alternate suggestions: split(where:), split(separatingWhere:).

The sort(by:) family

…seems fine, is slightly confusable with a “sort by key” API, but at least has a different type signature.

if roots.contains(where: isPrime) {

I really don’t like this one; it feels like it’s missing a noun. It certainly isn’t a valid English phrase. If we were naming an intersects(_:)-like feature, we might call it `roots.containsAny(of: primes)` or `roots.contains(anyOf: primes)`.

Alternate suggestions: containsAny(where:), contains(anyWhere:)

if expected.elementsEqual(actual, by: haveSameValue)

I tend to agree with the pre-review commentary <http://thread.gmane.org/gmane.comp.lang.swift.evolution/22188> that “by:” doesn’t actually fit here. The difference between elementsEqual(_:by:) / starts(with:by:) and the sort(by:) family is that “sort by…” and “order by…” are both things people actually say, but “equal by…” is not. Then again, when they say those things, they’re either talking about a key (“sort by name”) or a general comparison (“sort by compar[ing] names”, <=> rather than <).

let sum = measurements.reduce(0, +)

It concerns me that we can’t think of a label for this operation; it implies we can’t think of an English sentence to describe it. “Reduce this collection to a single element by combining using +.” reduce(startingWith:combiningWith:) is pretty unwieldy, though.

Jordan


(Karl) #6

The goal of the review process is to improve the proposal under review through constructive criticism and contribute to the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?

+1. Cleans things up a bit. I particularly like that the new names tend to be shorter.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Sure, it doesn’t hurt and it tidies things up quite nicely.

  * Does this proposal fit well with the feel and direction of Swift?

Very much so. It removes verbosity but doesn’t lose clarity.

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Doesn’t really apply. We’re talking about Swift; this feels closer to what Swift’s own standard library should look like.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read the review, participated in the initial review on GitHub and earlier discussions so I was already familiar with it.


(Taras Zakharko) #7

  * What is your evaluation of the proposal?

In general +1, except maybe the change of to isOrderedBefore: by: in sort-related functions. I fear that the new label makes it less clear that the ordering relation is that of strict precedence. It can be particularly confusing for people used to other APIs.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Well, in the end its an arbitrary decision. Either one works on practice. Personally, I don’t see the issue as being very significant.

  * Does this proposal fit well with the feel and direction of Swift?

Yes, it follows the spirit of the naming guidelines

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A glance

···

On 06 Jul 2016, at 01:10, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0118: Closure Parameter Names and Labels" begins now and runs through July 11. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and contribute to the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?
  * Is the problem being addressed significant enough to warrant a change to Swift?
  * Does this proposal fit well with the feel and direction of Swift?
  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #8

  * What is your evaluation of the proposal?

Strong +1. Overall I like all of the changes listed in this proposal, with some commentary below.

lines.split(whereSeparator: isAllWhitespace)

I think this is ok, but I'd also be fine with split(where:).

if roots.contains(where: isPrime) {

I strongly prefer this to the alternatives suggested in this thread. Not only is Erica right about "anywhere" being visible in containsAny(where:) or contains(anyWhere:), but those names also just feel unnecessarily cumbersome. Not only that, but using contains(where:) would be more consistent with first(where:) (which is a strongly related function; contains(where:) returns true iff first(where:) returns non-nil).

let sum = measurements.reduce(0, +)

I'm very happy to see the closure name being removed here. I never thought the name was pulling any weight, since this function is already a term of art rather than being an english sentence.

  * Is the problem being addressed significant enough to warrant a change to Swift?

Yes.

  * Does this proposal fit well with the feel and direction of Swift?

Yes.

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Other languages don't typically have external parameter names.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick reading of the proposal and of the thread to date.

-Kevin Ballard

···

On Tue, Jul 5, 2016, at 04:10 PM, Chris Lattner wrote:


(Dave Abrahams) #9

No, you're right; fixed, thanks!

···

on Wed Jul 06 2016, David Rönnqvist <swift-evolution@swift.org> wrote:

   * What is your evaluation of the proposal?

+1. These look like good improvements to me. (I'm also happy that the map/filter/reduce naming was left out of this proposal)

One thing I was wonder about was the capitalization of UTF8 in the
first example. Shouldn't that be s.withUTF8Buffer(processBytes)instead
of s.withUtf8Buffer(processBytes) or am I confusing Swift's
conventions with Cocoa's?

--
Dave


(Dave Abrahams) #10

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md ]

Hi, Dave, Dmitri, Max. Sorry I didn’t make the pre-review commentary
thread. I find I’m still not happy with several of these names,
although there are many other improvements. Stepping back, I think the
Swift API guidelines just don’t do a good job with closure
parameters. We should have naming guidelines for strategy closures and
for callbacks.

lines.split(whereSeparator: isAllWhitespace)

This reads like an English sentence, but it doesn’t have the correct
meaning for me. This implies a structure that has a pre-existing
“separator", and checks if that separator matches the predicate,
rather than searching for an element that matches the predicate, and
splitting on that. I realize that the former reading doesn’t make much
sense as a function, but it’s still impeded my understanding more than
helping it along.

Alternate suggestions: split(where:), split(separatingWhere:).

Split(where:) fails to imply that there are separators (and that some
elements would be omitted from the result), but we considered the second
one. I like the way it reads better, but “whereSeparator” has the
advantag of containing the word “separator” that's used in the
predicate-free version. For me it's a bit of a toss-up.

The sort(by:) family

…seems fine, is slightly confusable with a “sort by key” API, but at
least has a different type signature.

Yes, well, fortunately, Bool is not Comparable, or we would have a
problem when we want to introduce that API.

if roots.contains(where: isPrime) {

I really don’t like this one; it feels like it’s missing a noun. It
certainly isn’t a valid English phrase. If we were naming an
intersects(_:)-like feature, we might call it `roots.containsAny(of:
primes)` or `roots.contains(anyOf: primes)`.

I agree.

Alternate suggestions: containsAny(where:), contains(anyWhere:)

The second one, IMO.

if expected.elementsEqual(actual, by: haveSameValue)

I tend to agree with the pre-review commentary
<http://thread.gmane.org/gmane.comp.lang.swift.evolution/22188> that
“by:” doesn’t actually fit here. The difference between
elementsEqual(_:by:) / starts(with:by:) and the sort(by:) family is
that “sort by…” and “order by…” are both things people actually say,
but “equal by…” is not. Then again, when they say those things,
they’re either talking about a key (“sort by name”) or a general
comparison (“sort by compar[ing] names”, <=> rather than <).

  names1.elementsEqual(names2, by: {$0.lowercase == $1.lowercase})

reads OK to me.

let sum = measurements.reduce(0, +)

It concerns me that we can’t think of a label for this operation; it
implies we can’t think of an English sentence to describe it. “Reduce
this collection to a single element by combining using +.”
reduce(startingWith:combiningWith:) is pretty unwieldy, though.

IMO, if you read the doc comments for reduce, they push pretty strongly
toward renaming it to “accumulate,” if you can tolerate the loss of the
term-of-art. That, and the traditional usage-of-art, make me
unconcerned that there's no good label while it's called “reduce.”
FWIW.

···

on Thu Jul 07 2016, Jordan Rose <jordan_rose-AT-apple.com> wrote:

--
Dave


(Erica Sadun) #11

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md ]

Hi, Dave, Dmitri, Max. Sorry I didn’t make the pre-review commentary thread. I find I’m still not happy with several of these names, although there are many other improvements. Stepping back, I think the Swift API guidelines just don’t do a good job with closure parameters. We should have naming guidelines for strategy closures and for callbacks.

lines.split(whereSeparator: isAllWhitespace)

This reads like an English sentence, but it doesn’t have the correct meaning for me. This implies a structure that has a pre-existing “separator", and checks if that separator matches the predicate, rather than searching for an element that matches the predicate, and splitting on that. I realize that the former reading doesn’t make much sense as a function, but it’s still impeded my understanding more than helping it along.

Alternate suggestions: split(where:), split(separatingWhere:).

I think split(where:) is the cleaner and more elegant choice.

(But get ready, I'm about to defend Dave A's decisions from here forward)

The sort(by:) family

…seems fine, is slightly confusable with a “sort by key” API, but at least has a different type signature.

The obvious choices are `by`, `where`, and `with` but I like `by`. It tells the
story that this is a closure that's going to do the donkey's work for the sort.

if roots.contains(where: isPrime) {

I really don’t like this one; it feels like it’s missing a noun. It certainly isn’t a valid English phrase. If we were naming an intersects(_:)-like feature, we might call it `roots.containsAny(of: primes)` or `roots.contains(anyOf: primes)`.

This isn't perfect but it's a lot cleaner than a lot of the alternatives that came up
in discussion. I'm happy with this one too. It differentiates contains(_:slight_smile: from
contains(where:) and describes what the param does. I think it's solid enough
to hold its weight in the language.

Alternate suggestions: containsAny(where:), contains(anyWhere:)

Let me point out that "anywhere" jumps out, taking away from any positives this
would add. I get your point, but once you see "anywhere", you can't unsee it.

if expected.elementsEqual(actual, by: haveSameValue)

I'm okay with `by`. I'm okay with `where`. But I think `by` is better in this
case because you're doing something to figure out.

I tend to agree with the pre-review commentary <http://thread.gmane.org/gmane.comp.lang.swift.evolution/22188> that “by:” doesn’t actually fit here. The difference between elementsEqual(_:by:) / starts(with:by:) and the sort(by:) family is that “sort by…” and “order by…” are both things people actually say, but “equal by…” is not. Then again, when they say those things, they’re either talking about a key (“sort by name”) or a general comparison (“sort by compar[ing] names”, <=> rather than <).

But you do say "equate by"

let sum = measurements.reduce(0, +)

It concerns me that we can’t think of a label for this operation; it implies we can’t think of an English sentence to describe it. “Reduce this collection to a single element by combining using +.” reduce(startingWith:combiningWith:) is pretty unwieldy, though.

This is short, sweet, and beautiful, lending itself to simple operators. When a
more complex routine is needed, it's perfect for trailing closures. I think it's a
great choice.

-- E, really positive about this proposal

···

On Jul 7, 2016, at 5:03 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:


(Dave Abrahams) #12

  * What is your evaluation of the proposal?

In general +1, except maybe the change of to isOrderedBefore: by: in
sort-related functions. I fear that the new label makes it less clear
that the ordering relation is that of strict precedence. It can be
particularly confusing for people used to other APIs.

Here's the way I rationalize that to myself, FWIW:

`isOrderedBefore` was mostly a hint for the *author* of the call, to
make sure they didn't use something like `<=`. We help the author by
building the strictness requirement into the *name* of the parameter,
where it will show up in code completion. For the *reader* of the call,
once the author gets the call right, it's not particularly helpful to
know that strictness was a requirement, and what's required to convey
that information is heavy enough that it harms readability. Now, you
could argue that for *maintainers* without code completion or easy
access to the documentation, the label was helpful. Personally, I felt
that sort of specificity just ended up being “overly fussy,” but it's
nuanced.

···

on Thu Jul 07 2016, Taras Zakharko <swift-evolution@swift.org> wrote:

  * Is the problem being addressed significant enough to warrant a change to Swift?

Well, in the end its an arbitrary decision. Either one works on practice. Personally, I don’t see the issue as being very significant.

  * Does this proposal fit well with the feel and direction of Swift?

Yes, it follows the spirit of the naming guidelines

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A glance

On 06 Jul 2016, at 01:10, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of "SE-0118: Closure Parameter Names and Labels" begins now and runs through July 11. The proposal is available here:

  https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

  https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager.

What goes into a review?

The goal of the review process is to improve the proposal under
review through constructive criticism and contribute to the
direction of Swift. When writing your review, here are some
questions you might want to answer in your review:

  * What is your evaluation of the proposal?
  * Is the problem being addressed significant enough to warrant a change to Swift?
  * Does this proposal fit well with the feel and direction of Swift?
  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

  https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Chris Lattner
Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave


(Jordan Rose) #13

Your comments and Erica’s comments have convinced me, or at least lessened my concerns, on all but split(…), but the higher-level point that we need naming guidelines for different kinds of closure parameters still stands.

Jordan

···

On Jul 7, 2016, at 17:57, Dave Abrahams <dabrahams@apple.com> wrote:

on Thu Jul 07 2016, Jordan Rose <jordan_rose-AT-apple.com <http://at-apple.com/>> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0118-closure-parameter-names-and-labels.md ]

Hi, Dave, Dmitri, Max. Sorry I didn’t make the pre-review commentary
thread. I find I’m still not happy with several of these names,
although there are many other improvements. Stepping back, I think the
Swift API guidelines just don’t do a good job with closure
parameters. We should have naming guidelines for strategy closures and
for callbacks.

lines.split(whereSeparator: isAllWhitespace)

This reads like an English sentence, but it doesn’t have the correct
meaning for me. This implies a structure that has a pre-existing
“separator", and checks if that separator matches the predicate,
rather than searching for an element that matches the predicate, and
splitting on that. I realize that the former reading doesn’t make much
sense as a function, but it’s still impeded my understanding more than
helping it along.

Alternate suggestions: split(where:), split(separatingWhere:).

Split(where:) fails to imply that there are separators (and that some
elements would be omitted from the result), but we considered the second
one. I like the way it reads better, but “whereSeparator” has the
advantag of containing the word “separator” that's used in the
predicate-free version. For me it's a bit of a toss-up.


(Dave Abrahams) #14

This reads like an English sentence, but it doesn’t have the correct
meaning for me. This implies a structure that has a pre-existing
“separator", and checks if that separator matches the predicate,
rather than searching for an element that matches the predicate, and
splitting on that. I realize that the former reading doesn’t make much
sense as a function, but it’s still impeded my understanding more than
helping it along.

Alternate suggestions: split(where:), split(separatingWhere:).

Split(where:) fails to imply that there are separators (and that some
elements would be omitted from the result), but we considered the second
one. I like the way it reads better,

Actually I take that back. It still fails to imply that there are
separators and elements may be omitted.

but “whereSeparator” has the advantag of containing the word
“separator” that's used in the predicate-free version. For me it's a
bit of a toss-up.

Your comments and Erica’s comments have convinced me, or at least
lessened my concerns, on all but split(…), but the higher-level point
that we need naming guidelines for different kinds of closure
parameters still stands.

Great, please propose some!

My feeling is that we can only discover what these guidelines should be
by solving individual naming problems (in groups, as here).

···

on Fri Jul 08 2016, Jordan Rose <jordan_rose-AT-apple.com> wrote:

--
Dave


(Erica Sadun) #15

split(withLossySeparator:) ??

(I hate this but there's a point to be made that naming dangerous
operations trumps simple names and the other optional external
parameter names aren't exactly trim)

-- E

···

On Jul 8, 2016, at 10:56 AM, Dave Abrahams <dabrahams@apple.com> wrote:

on Fri Jul 08 2016, Jordan Rose <jordan_rose-AT-apple.com> wrote:

This reads like an English sentence, but it doesn’t have the correct
meaning for me. This implies a structure that has a pre-existing
“separator", and checks if that separator matches the predicate,
rather than searching for an element that matches the predicate, and
splitting on that. I realize that the former reading doesn’t make much
sense as a function, but it’s still impeded my understanding more than
helping it along.

Alternate suggestions: split(where:), split(separatingWhere:).

Split(where:) fails to imply that there are separators (and that some
elements would be omitted from the result), but we considered the second
one. I like the way it reads better,

Actually I take that back. It still fails to imply that there are
separators and elements may be omitted.

but “whereSeparator” has the advantag of containing the word
“separator” that's used in the predicate-free version. For me it's a
bit of a toss-up.