This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-10-31
Channels
- # announcements (5)
- # babashka (105)
- # beginners (92)
- # calva (77)
- # cider (17)
- # cljdoc (8)
- # cljs-dev (8)
- # cljsrn (8)
- # clojure (272)
- # clojure-dev (25)
- # clojure-europe (5)
- # clojure-italy (6)
- # clojure-nl (7)
- # clojure-norway (3)
- # clojure-uk (108)
- # clojurescript (326)
- # code-reviews (4)
- # cursive (6)
- # datomic (37)
- # duct (5)
- # emacs (14)
- # fulcro (23)
- # graphql (1)
- # juxt (1)
- # kaocha (2)
- # leiningen (10)
- # malli (9)
- # music (1)
- # nrepl (12)
- # pathom (21)
- # pedestal (2)
- # planck (4)
- # quil (3)
- # reitit (29)
- # rewrite-clj (10)
- # shadow-cljs (82)
- # spacemacs (29)
- # sql (6)
- # tools-deps (19)
måning
then, when the company realises your value, they'll beg to hire you back at x3 your current salary 🙂
is anyone aware of a good tinfoil hat emoji?
kinda thing
(obviously the goverment replaced the tin with aluminium many year ago) (and then hushed it up)
https://github.com/Crissov/unicode-proposals/issues/188 despite popular public requests for this emoji, the govt is clearly holding this up to prevent the spread of this knowledge.
(let [results (db/find-starships)]
(if (seq results) (println "resistance is futile") (println "engage")))
The api returns a vector...however, so long as a collection is maintained, it doesn't have to be a vector onwards.
Oh, I guess you are using it again in the actual code, else why check it for nil? Nevermind, ignore me 🙂
However, some->
is another fun way to do this if you don't need the intermediate name.
No problem. It's highly subjective and a little inane possibly, but I have this weird thing about trying to avoid let
if I can. It's a bit hard to articulate why, but I think it's probably because it's a feature that is often used as a way to 'hide' complexity. I see a lot of functions that begin with 5 or 6 let
s. I've even seen nested let's more than a few times. If nothing else, it's a fun little intellectual exercise to try to come up with ways to avoid them 🙂
https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/not-empty can also be helpful
altthough that's just a
(when (seq coll) coll))
now I Iook at itseq
as a synonym for not-empty
I find unintuitive though
I used to be a fan of slave pestov’s factor back in the day… The dev env is pretty interesting: https://factorcode.org/ Plus like forth it has comparable meta programming capabilities to lisp.
is that considered an accident-of-history?
not-empty
is, in a very mild sense, a bit misleading because it sounds like it's a pure predicate, but it's not. It reads perfectly fine if treated as a predicate but looks a bit weird if used as in my (some-> ...)
example above I think.
I think seq
is probably better if you are doing anything other than using the truthy/falsey properties of the return value.
In fact, i'd imagine that if not-empty
was created today it would be called not-empty?
and cast result to boolean.
It was a very deliberate choice in the early days… If you dig through the google group you can probably find the rationale…
Essentially though I think it boils down to:
1. seq
is one of the most central concepts in clojure’s sequence library/design, and it is guaranteed to return nil
for any empty collection.
2. Collection’s aren’t themselves seq
s you ask a collection to give you a seq
on it.
3. It’s idiomatic clojure as in most languages to test for the presence of a value rather than its absence i.e. (when thing ,,,)
is usually more idiomatic than (when (not no-thing) ,,,)
Therefore (if (seq coll) ,,, ,,,)
is idiomatic. It’s also simpler than not-empty
as not-empty
uses seq
, and more efficient, not that that’s important for most cases.
not-empty
is very useful in threaded expressions where you want either the original thing (collection, string, etc) or nil
if it was empty.
A predicate returning Boolean doesn't help you there.
Otherwise you need to drop this into the pipeline (as-> $ (when (seq $) $))
@seancorfield Good point. I hadn't thought not-empty
maintaining the type.
We use it a lot in form data validation where we treat an empty form field (`""`) as "not provided"
seq
will lose the collection type for sure; as do most sequence functions; as they all call seq
internally
@seancorfield presumably you don't write that in terms of blank?
because you don't consider " "
as not provided?
No, we don't use blank?
because that returns Boolean.
We tend to do (some-> req :params :the-field (str/trim) (not-empty))
-- produces nil
if field was not provided or was blank (zero or more whitespace, nothing else). Since we always want the field trimmed if it is present.
So " Hello! "
produces "Hello!"
and " "
produces nil
.
(defn not-blank
[s]
(when-not (string/blank? s) s))
is function I almost always end up writingbut there is an infinite panoply of blank strings
how d'ye like yer whitespace
It's sort of a funny thing because people use not-empty
because it's considered more descriptive and clearer than seq
in the simple predicate case, but if anything it has behaviour that a little more obscure and subtle than seq
at a glance. Not to mention the fact that we do have both empty
and empty?
but we don't have both not-empty
and not-empty?
🙂
Ah, because empty
checks (instance? clojure.lang.IPersistentCollection coll)
explicitly...
(I do not use not-empty
as a predicate)
@seancorfield was referring to the original part of the discussion on this. Seemed to be the argument was that not-empty
is more readable in the simple predicate case. Completely understand your way of using it but the behaviour you are leaning on there is definitely a bit more subtle.
Entirely valid, but not immediately obvious from the naming of the function I think, that's all.
re-reading my code, it does feel a bit strange that I have not-empty
for it can change (the result) depending on whether the collection is empty or not
@dharrigan There is a semantic difference. In both cases you'll get nil
if the collection is empty. In the second case you will always get a sequence if it is non-empty. In the first case you will get back the original collection (vector, list, whatever).
If you're only going to use results
in a sequence context, that's fine.
Yes, understood, in this case yes, not really bothered if it comes back as a list rather than a vector
seq
changes the type, not-empty
preserves the type.
(you said "`not-empty` for it can change" which isn't true)
We aim to please 🙂
I used to love those ads...
@dharrigan could be worth considering whether it makes sense to have all of your db functions just return a seq. If you standardise on the idea that "no matching results" = falsey (i.e. nil), these problems disappear anyway.
If you need constant time lookup of result in particular index positions, fair enough but that's pretty rare I guess. Also you can do cursor style magic with lazy sequences and keep all that internal to the db namespace. Worth considering perhaps.
@wesley.hall thanks - great advice!
@wesley.hall with next.jdbc
the default is vector of hash map for fully-realized result sets (you can use plan
to operate via reduce
without realizing the result set). So @dharrigan’s DB functions will return []
when there are no results (not nil
).
yes, that's precisely what is happening, an empty vector when no starships (they've all been assimilated by the borg via a nasty truncate statement!)
The reason is so that the returned type is predictably a vector so the following is guaranteed to preserve order (into (jdbc/execute! ,,,) (jdbc/execute! ,,,))
If that could return nil
, then if the first query did not match, the overall result would be a list of the second query's results in reverse order 😞
@seancorfield Ahh, that's also pretty subtle, but makes sense. I haven't used the library myself but I get the reasoning there.
(I've been down this path before in production code where DB ops could return nil
and it was a mess -- I ended up with a lot of wrappers that did (or (sql/execute ,,,) [])
🙂 )
Makes sense. I've done a fair bit with dynamodb where paginating results is the norm and so dealing with lazy sequences and batch loading is kind of the bread and butter but can absolutely see where you're coming from here.
> Where do you host your private JARs? Using JitPack https://jitpack.io/, it’s nice and it has low maintenance overhead. Sometimes it’s a bit fickle: for example, the way it works, it picks up the version of a JAR from a git tag or a SHA of your GitHub hosted repo. Which is fine, except, the JAR that you want, regardless of how you reference it, is not built by default, but only when you request it for the very first time. So if you push some build where you’re referencing the release, that was never referenced before, the build will fail, because JitPack will take time to build the release on the first invocation. But once it’s built, then it’s there and you can fetch it when needed. Sometimes, this fetching can fail due to … well, sometimes it just fails, no logical reason. But because you don’t have to host anything yourself, it’s a sort of thing you can tolerate. Looking at GitHub’s new offering, the package registry: https://github.com/features/package-registry Any of you played with it?
@wesley.hall because of resource handling (JDBC connections etc), it's easier to use plan
to reduce a result set (eagerly) which actually allows you to stream very large result sets out of the database lazily. Counterintuitive, I know, but that's JDBC for you 😁
If you're exposing explicit pagination to a client, that's different of course (and you end up just requesting chunks of records at a time in separate operations).