Fork me on GitHub
#clojure-uk
<
2019-10-31
>
dharrigan06:10:38

Good Morning

thomas08:10:53

mogge 😼

folcon10:10:27

Morning =)…

maleghast10:10:34

Morning Everyone

thomas10:10:37

Great... on my last day things are broken :face_with_symbols_on_mouth:

thomas10:10:51

and I have no idea what is going wrong... 😞

dharrigan10:10:14

leave now

😂 4
dharrigan10:10:40

then, when the company realises your value, they'll beg to hire you back at x3 your current salary 🙂

Ben Hammond10:10:21

is anyone aware of a good tinfoil hat emoji?

Ben Hammond11:10:21

(obviously the goverment replaced the tin with aluminium many year ago) (and then hushed it up)

dominicm11:10:41

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.

4
👍 4
😂 4
dharrigan12:10:50

Is there a better pattern for this?

dharrigan12:10:54

(let [results (db/find-starships)]
    (if (seq results) (println "resistance is futile") (println "engage")))

dharrigan12:10:14

where db/find-starships can return a nil vector if no results found

dominicm12:10:12

Oh, you need to seq it because it's empty. Do you care if it's a vector?

dharrigan13:10:23

The api returns a vector...however, so long as a collection is maintained, it doesn't have to be a vector onwards.

dominicm13:10:13

when-let [result (seq (do-thing))]

✔️ 4
dharrigan13:10:19

works a charm, grazie!

Wes Hall13:10:27

If you are only using the results value once, do you even really need to let it?

Wes Hall13:10:28

Oh, I guess you are using it again in the actual code, else why check it for nil? Nevermind, ignore me 🙂

Wes Hall13:10:59

However, some-> is another fun way to do this if you don't need the intermediate name.

✔️ 4
Wes Hall13:10:56

(some-> db/find-starships seq whatever-fn)

dharrigan13:10:32

thanks for the tip! 🙂

Wes Hall13:10:04

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 lets. 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 🙂

Ben Hammond13:10:41

altthough that's just a

(when (seq coll) coll))
now I Iook at it

Ben Hammond13:10:51

seq as a synonym for not-empty I find unintuitive though

acron14:10:14

I prefer 'not-empty'

👍 4
rickmoynihan15:10:22

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.

dharrigan13:10:16

@benha nice! I can see from the source that it calls seq

dharrigan13:10:30

but not-empty is easier to comprehend 🙂

rickmoynihan15:10:23

seq is more idiomatic in clojure

4
Ben Hammond15:10:13

is that considered an accident-of-history?

dominicm16:10:24

don't think so

Wes Hall16:10:49

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.

Wes Hall16:10:57

I think seq is probably better if you are doing anything other than using the truthy/falsey properties of the return value.

Wes Hall16:10:48

In fact, i'd imagine that if not-empty was created today it would be called not-empty? and cast result to boolean.

thomas16:10:19

ttfn... see you all next week from the other side.

Wes Hall16:10:58

Thomas wins, "most halloween" message of the day award.

😂 4
rickmoynihan16:10:48

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 seqs 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.

seancorfield16:10:30

not-empty is very useful in threaded expressions where you want either the original thing (collection, string, etc) or nil if it was empty.

seancorfield16:10:58

A predicate returning Boolean doesn't help you there.

seancorfield16:10:39

Otherwise you need to drop this into the pipeline (as-> $ (when (seq $) $))

Wes Hall16:10:50

@seancorfield Good point. I hadn't thought not-empty maintaining the type.

seancorfield16:10:29

We use it a lot in form data validation where we treat an empty form field (`""`) as "not provided"

rickmoynihan16:10:28

seq will lose the collection type for sure; as do most sequence functions; as they all call seq internally

Wes Hall16:10:24

@seancorfield presumably you don't write that in terms of blank? because you don't consider " " as not provided?

seancorfield16:10:23

No, we don't use blank? because that returns Boolean.

seancorfield16:10:47

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.

seancorfield16:10:34

So " Hello! " produces "Hello!" and " " produces nil.

dominicm16:10:47

not-empty is the opposite of empty

Ben Hammond16:10:21

(defn not-blank
  [s]
  (when-not (string/blank? s) s))
is function I almost always end up writing

dominicm16:10:06

You should write a blank function to go with it

dominicm16:10:17

It just returns a blank string

dominicm16:10:31

That might be empty, if that works on strings

Ben Hammond16:10:38

but there is an infinite panoply of blank strings

Ben Hammond16:10:50

how d'ye like yer whitespace

dominicm16:10:03

Just return one of them

Wes Hall16:10:16

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? 🙂

seancorfield16:10:24

Ah, because empty checks (instance? clojure.lang.IPersistentCollection coll) explicitly...

seancorfield16:10:46

(I do not use not-empty as a predicate)

Wes Hall16:10:22

@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.

Wes Hall16:10:55

Entirely valid, but not immediately obvious from the naming of the function I think, that's all.

dharrigan20:10:28

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

dharrigan20:10:35

(if-let [results (not-empty (db/find-starships))]

dharrigan20:10:58

perhaps using seq as originally suggested makes it clearer

dharrigan20:10:41

(if-let [results (seq (db/find-starships))]

seancorfield20:10:05

@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).

seancorfield20:10:25

If you're only going to use results in a sequence context, that's fine.

dharrigan20:10:50

Yes, understood, in this case yes, not really bothered if it comes back as a list rather than a vector

seancorfield20:10:52

seq changes the type, not-empty preserves the type.

dharrigan20:10:15

understood 🙂

seancorfield20:10:16

(you said "`not-empty` for it can change" which isn't true)

dharrigan20:10:35

yes, badly worded

dharrigan20:10:45

thanks for keeping me straight 🙂

seancorfield20:10:54

We aim to please 🙂

dharrigan20:10:14

well, you are, your next jdbc library is grrrreat

dharrigan20:10:18

as in tony the tiger grrrreat

seancorfield20:10:27

I used to love those ads...

Wes Hall20:10:17

@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.

Wes Hall20:10:02

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.

jasonbell20:10:00

Morning

😆 4
dharrigan20:10:43

@wesley.hall thanks - great advice!

seancorfield21:10:30

@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).

dharrigan21:10:10

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!)

seancorfield21:10:38

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! ,,,))

seancorfield21:10:15

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 😞

Wes Hall21:10:18

@seancorfield Ahh, that's also pretty subtle, but makes sense. I haven't used the library myself but I get the reasoning there.

seancorfield21:10:29

(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 ,,,) []) 🙂 )

Wes Hall21:10:22

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.

dotemacs22:10:24

> 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?

seancorfield22:10:45

@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 😁

seancorfield22:10:25

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).