Welcome! Please see the About page for a little more info on how this works.

+10 votes
in Macros by

Proposal:

Add when-seq and if-seq as short-hands for (when-let [x (seq ...)]) and (if-let [x (seq ...)]).

What problem does this solve? It's just a convenience for dealing with functions that either return nil or empty collections.

2 Answers

+2 votes
by
by
I like it! Would it be better though to make it a shorthand for `(when-let [x (not-empty ...)] x)`? so x is still eg a string or vector.
by
I checked my local .m2 with this script: https://gist.github.com/borkdude/40a3670336a91fb4cdc3528c565b7241

I found 1700 times `(when-let [x (seq ...)] ...)` vs 166 times ` (when-let [x (not-empty ...)] ...)`.

However, we could just bind the variable to the original after calling seq on it. But this would have the downside of having to call seq on it again if you really wanted a seq.
0 votes
by

Do you have any frequency data on how common this pattern is?

Do these functions exist in other utility libraries? If so, are they used?

by
The only lib I found with these was vodori/missing:

https://github.com/vodori/missing/blob/develop/src/missing/core.clj#L159

But I can't find any uses of those.
by
I can do a more thorough analysis using grasp tomorrow (which will also match usages spread over multiple lines).

The reason I needed this was when I created a function that returned a seq, but I also wanted it to be used as a predicate without the user needing to call seq on it in a when-let. If the above macro existed in core I could just recommend to use that and not worry about my return value.
by
I'm finding about 1700 occurrences in my local .m2 using this script:

https://gist.github.com/borkdude/40a3670336a91fb4cdc3528c565b7241

Just make it executable and run from a directory. The script will scan the dir recursively for sources/jars.
by
I like saving 6 keystrokes, and in my opinion "fn" in place of "function" or "lambda" fully pays the price of admission, but I foresee some costs to if-seq/when-seq.  Mainly the erosion of other good things about Clojure.

I like that seq is a simple idiom that appears in all contexts.
I like that I can think about conditional logic without worrying about complected transformations.  
I like the appearance (even if it is a tiny bit false) that the sequence library is distinct from the language.
I am in awe of Clojure's existing bargains with truthiness.

To ameliorate my complaint about refactoring, "if-seq" could be called "if-let-seq" and a "let-seq" could be added, so adding/removing a condition would involve only the "if-" or "when-" prefix.  But there you'd've lost 4 of the 6 saved keystrokes AND forgotten the lessons of letfn AND gone down a slippery slope toward an alternate universe of forms with "-seq" in their name.  

Next, "some-seq", "remove-seq", "take-while-seq", etc., to similarly compose seq with their embedded test.  

To redeem myself after all this negativity, I will say that if someone is eager to save me some keystrokes, I would love a Java var-args interop that didn't make me write out "maaaakkkkeee aaannnn arrraaaayyy ooffff ttthheee tttthhiiiinnngggsss".
by
The name follows prior naming of `if-some`. Instead of `some?` we use the predicate `seq`.
To follow the behavior of `if-some` the binding should maybe not contain the `seq`-ed object, but the original object. This would also preserve the type information like @eval suggested.
...