Fork me on GitHub
#beginners
<
2020-08-15
>
seancorfield00:08:08

Just based on some of the examples in the next.jdbc/plan documentation, I'd probably be inclined toward something like this @clojurians-slack100

user=> (def r-first #(reduce (fn [_ x] (reduced x)) nil %))
#'user/r-first
user=> (r-first (eduction (comp (map str/upper-case) (filter #(doto % println))) [:foo :bar :baz]))
:FOO
":FOO"
user=>

walterl00:08:31

Very interesting. Thank you!

seancorfield00:08:22

(I changed some? to #(doto % println) just to verify it was only processing one item of the sequence)

walterl00:08:12

Very interesting use of reduce there 🎉

seancorfield00:08:44

next.jdbc/plan returns a reducible -- an IReduceInit -- so the natural way to get just the first row of a streaming result set is that (reduce (fn [_ x] (reduced x)) nil the-plan)

walterl00:08:08

"natural" you say? 😜

phronmophobic00:08:20

I think it can be further "reduced" (EDIT: this isn't quite right. see @seancorfield's fix below):

(require '[clojure.string :as str])
(def step
  (comp
   (filter some?)
   (map str/upper-case)))

(let [rf (step (fn [_ x]
                 (reduced x)))]
  (reduce rf
          [nil nil :foo :bar :baz]))

phronmophobic01:08:40

I guess that's just a rewrite of @seancorfield’s response?

phronmophobic01:08:22

the eduction threw me off

seancorfield01:08:08

I hadn't thought of calling the step function on the r-first internals...

phronmophobic01:08:03

yea, my sense was that a transducer makes a step function, and that you just want to keep calling the step function until you get a result.

phronmophobic01:08:43

my intuition tells me it could be simplified even further since you're only grabbing one result max and never have to combine it with previous results. in that sense, reduce might still be overkill

phronmophobic01:08:18

ie. the first argument to the rf function is completely ignored

seancorfield01:08:58

Yeah, every time I write it -- and I write it more often than I'd like -- I feel like there should be some function like it in core 🙂

seancorfield01:08:08

Ah, but now you've reversed the comp order (but I think you're right: the filter some? should have come first, right?).

phronmophobic01:08:13

yea, (filter some?) is only filtering nils, so it would never get called since str/upper-case would fail before that

walterl01:08:43

OK, I simplified my real-world problem... badly. The function being mapped is more complicated, and it is the output from that map that needs to be filtered, not the inputs.

phronmophobic01:08:34

well, the example should work regardless of the step, so your simplification was useful for making the question easy to follow

seancorfield01:08:26

@smith.adriane Actually that doesn't work as shown:

user=> (def step
         (comp
           (filter some?)
           (map str/upper-case)))
#'user/step
user=> (let [rf (step (fn [_ x] (reduced x)))]
          (reduce rf [:foo :bar :baz]))
":BAR"
user=>

seancorfield01:08:48

(you need the nil value for init to make that work)

seancorfield01:08:04

user=> (def step
         (comp
           (filter some?)
           (map str/upper-case)))
#'user/step
user=> (let [rf (step (fn [_ x] (reduced x)))]
          (reduce rf nil [:foo :bar :baz]))
":FOO"
user=>

fredmonroe04:08:03

i am having a hard time googling this... in hiccup (re-frame/reagent) - what does: ":>" mean - sometimes i'll see something like: [:> Button {:color "primary"} "button text"] as opposed to: [:Button ..etc] i'm not sure when to use ":>" but it seems like if i don't the styling doesn't get applied

fredmonroe04:08:13

answering my own question (i searched this slack for ":>") - here is a handly link that talks about hiccup interop with react: https://github.com/reagent-project/reagent/blob/master/doc/InteropWithReact.md

fredmonroe04:08:24

so "[:> foo...." translates to "[(reagent/adapt-react-class foo)..."

David Pham06:08:26

I am sorry if this sounds really trivial. Over time, I built a library [more a collection of namespaces] in CLJS. I would like to share it internally at my company. There are several possibilities: using deps.edn local/root, and git. The third possibility is about packaging everything into a JAR and ask the user to maven install it. I wonder if anyone know where I could find the documentation or a tutorial on how to do the JAR process? Also, does what I am saying make sense?

RafaMedina16:08:32

Or a more scalable alternative, create a "company" repository

Jack Arrington17:08:16

This is a very small concern really, but is it stylistically odd in Clojure to insert a line break between "groups" of code inside a function? I feel like I have not seen this in anyone else's code

phronmophobic17:08:02

i do it all the time, especially in let statements

thom18:08:06

Totally acceptable, but also always worth wondering if the function has grown to justify extracting the subgroups into named functions of their own.

Jack Arrington18:08:42

@UTF99QP7V yeah, I get that and agree. Just a few cases of unavoidable complexity...at least for a Clojure newbie like me 🙂

Jack Arrington18:08:51

I will say I've naturally found myself doing it far less in Clojure, I think in part because the language itself is so concise, but also because it provides a richer set of primitives for working on data(structures). In an imperative language I often find myself grouping things into blocks when dealing with complicated loops and/or conditionals just to try and make my intent more clear. Clojure, I'm finding, naturally encourages me to think about what I'm doing instead of how, so the intent is more clear from the start.

seancorfield18:08:40

I tend to only do it in cond expressions, where the pairs of test/expr are already on separate lines, so I put a blank line after each test/expr pair so you can still see the structure at a glance.

Jack Arrington18:08:26

Funnily, a long-ish cond was actually the case that made me ask.

seancorfield18:08:48

I will occasionally do it in let statements that have distinct groups of (side-effecting) logic followed by a result expr -- so blank lines can make that clearer.

seancorfield18:08:05

Both things said, they do also say "refactor to a function".

seancorfield18:08:02

Where I will refactor: if each "chunk" wouldn't require a whole bunch of local data passed in as arguments.

seancorfield18:08:50

But if making those test/expr and/or chunks into functions produces long argument lists, and each chunk of code is only 2-3 lines, I'll leave them inline (with blank lines).

thom18:08:16

I do think sometimes you learn that a bunch of local data is actually a named Thing in your domain, and predicates and transformations around it give you deeper insight into the underlying problem which can pay off even more later. Obv you can go over the top here but I do try to leave a small portion of my brain looking for these opportunities.

seancorfield18:08:49

Good advice @UTF99QP7V, yes! Refactoring code can sometimes provide new insights into our data.