Fork me on GitHub
#beginners
<
2019-05-14
>
haiyuan.vinurs09:05:51

how to navigate clojure code in emacs? cider ? or there maybe some other method

chokheli10:05:15

@haiyuan.vinurs haven't completely got a question.

lepistane10:05:03

@haiyuan.vinurs emacs and install cider that should be enough to get you going https://www.braveclojure.com/basic-emacs/

haiyuan.vinurs10:05:59

with cider that i must start a repl, is there a tool like global gtags that can parse the code statically

takamura.developer11:05:59

my normal workflow with cider is: open clojure project, start cider repl with :cider-jack-in, start to eval my code with :cider-eval-defun-at-point (mapped to ,ef hehe), finish the cider repl with C-c C-q

seancorfield16:05:03

@haiyuan.vinurs Clojure really expects to have a live REPL for all your workflows. Check out Stu Halloway's talk on REPL-Driven Development, and another one called Running With Scissors. Without a REPL, you'll struggle to work with Clojure. Like @ says: start a REPL at the beginning of your work and keep it open until you're done. I tend to keep my REPL running for days and days.

dharrigan15:05:56

I'm confused, how does (reduce conj () [1 2 3 4]) reverse the sequence? (yes, walking through 4clojure problems!)

dharrigan15:05:14

it's the reduce part that confused me, not the conj

dpsutton15:05:30

(conj (list 1) 2) in your repl

dpsutton15:05:03

and then (conj (conj (list 1) 2) 3) in your repl

lilactown15:05:00

the tricky thing is that (conj '(1) 2) appends while (conj [1] 2) prepends

dpsutton15:05:25

conj has performance guarantees not location guarantees. Vectors are easy to append to. lists are easy to prepend

dharrigan15:05:06

Ahh, I see, thank you Dan and Will. Both very insightful.

dharrigan15:05:16

I will ponder (hammock?) the code further 🙂

markmarkmark15:05:25

@dharrigan you can also replace the reduce with reductions to get an idea of what is happening at each step of the reduce.

dharrigan15:05:56

that's handy to know! thank you kindly!

idanmel16:05:12

I was confused about that one until I realized that the () was just the initial value for the conj... Realizing that (reduce conj () [1 2 3 4]) is the same as (reduce conj [() 1 2 3 4]) made it clearer for me

alexmiller16:05:56

often if a reduce is confusing you can s/reduce/reductions/ to get some clarity - (reductions conj () [1 2 3 4])

alexmiller16:05:12

shows you all the intermediate accumulator values

dharrigan16:05:02

That's a great tip!

kari.marttila18:05:55

I spent a nice debugging session with cognitect's AWS library wondering why its profile-credentials-provider cannot find my profile. Heh, a rookie mistake:

:aws-profile local-dynamodb
=> type: symbol (should be string, i.e.:)
:aws-profile "local-dynamodb"
But I got a chance to try Cursive debugger which was actually great and in the debugger I pretty soon figured out that
(get (config/parse f) profile-name
was returning nothing and therefore I realized soon that I had a symbol in configuration instead of string. Regarding my yesterday's question if it is possible to change environment variable value on the fly - I don't need to change it. I found out that Cognitect's AWS library provides a profile-credentials-provider - so, I can have both of my AWS profiles (local Docker DynamoDB profile and real AWS profile) in my confige.edn and using Mount switch the profile in the REPL without restart. Clojure is great!

kari.marttila18:05:11

And I really love Cursive. I created a "reset" command and a simple hot key for it - I'm able to "stop states, refresh namespaces, start states" with one keystroke, i.e. e.g. switch the data source from my local dynamodb docker container to the real AWS DynamoDB. Yihaa!

kari.marttila18:05:23

... and if you see Stuart Halloway there in Cognitect corridors send him my regards regarding his "Repl Driven Development" - it totally changed how I use REPL.

kari.marttila19:05:03

Having a scratch file where I write all my REPL stuff nowadays instead of writing them in the actual REPL editor really boosted my Clojure productivity. You guys at Cognitect could list some good beginner "must-to-watch-videos" 🙂

kari.marttila19:05:28

... or someone could write a book titled "50 Clojure coding and REPL tricks" - I surely would buy a copy. 🙂

kari.marttila19:05:20

If I have a vector like

[{:pgid {:S "1"}, :pgname {:S "Books-local-aws"}} {:pgid {:S "2"}, :pgname {:S "Movies-local-aws"}}]
... is there a fancy, Clojure idiomatic way, to turn it to this:
{"1" "Books-local-aws", "2" "Movies-local-aws"}
(I mean using the Clojure standard library without recur or reduce?) I should start to learn to use the standard library - I understood that the standard library often provides various transformations so that you don't need to loop yourself.

noisesmith19:05:30

why is reduce not idiomatic?

kari.marttila19:05:40

This works:

(reduce
        (fn
          [mymap item]
          (conj mymap {(get-in item [:pgid :S]) (get-in item [:pgname :S])}))
        {}
        items)

noisesmith19:05:44

it's the best way to make an associative object out of a seq

kari.marttila19:05:01

... but is there some standard library function that could be used instead?

noisesmith19:05:15

you can use assoc instead of conj

kari.marttila19:05:26

So, my reduce above is just fine?

noisesmith19:05:43

(conj foo {bar baz}) is the same as (assoc foo bar baz) except it creates a hash-map nobody needs

noisesmith19:05:03

yes, the reduce is fine, but I'd use assoc

kari.marttila19:05:48

So:

(reduce
        (fn
          [mymap item]
          (assoc mymap (get-in item [:pgid :S]) (get-in item [:pgname :S])))
        {}
        items)

noisesmith19:05:57

also, this is open to judgment, but (-> item :pgid :S) might be more readable than (get-in item [:pgid :S]) for this specific case

noisesmith19:05:49

a line break might help readability as well

(assoc foo
       bar baz)

kari.marttila19:05:21

So:

(reduce
        (fn
          [mymap item]
          (assoc mymap 
            (-> item :pgid :S) (-> item :pgname :S)))
        {}
        items)

kari.marttila19:05:37

Good that I asked. 🙂

kari.marttila19:05:42

So, occasionally if I'm wondering if my small code snippet is idiomatic Clojure or not, you guys don't mind me asking here? I learn a lot with you gurus. 🙂

manutter5119:05:10

This is exactly the right place for that sort of question

kari.marttila20:05:26

All right! I'll be a lot more active here in the future! And I'll be learning Clojure 2x than I previously did. Yihaa!

nedcg20:05:39

i would do this

noisesmith20:05:28

there's also #code-reviews but usually that's usually used for bigger things

noisesmith20:05:32

@nedcg my only problem with that is that putting an arg to a parent on the same line as a child that had a line-break in the middle feels strange

noisesmith20:05:06

to me a line break mid form introduces a new level of nesting, so keeping an upper level on the same line has a weird friction to it - but of course that's subjective

nedcg20:05:44

@noisesmith the line-break is for visually associate the operations over the same parameter(%2), it feels weird to me decompact with line-breaks such a simple function

noisesmith20:05:44

right, but {} and items are for reduce, not for #(assoc ...)

noisesmith20:05:08

so I would remove the line break, or add another for each arg to reduce

noisesmith20:05:45

but as I said at the start, it's subjective, we don't need to have consensus, but it's informative to hear each others reasons I think

nedcg20:05:31

absolutelly, thanks for the feedback

matt.kipps20:05:12

another way to implement this pattern is with (into {} (map (juxt key-fn val-fn) coll)), where key-fn and val-fn are the corresponding “extractions” on each value in coll

matt.kipps20:05:19

(->> items
     (map (juxt (comp :S :pgid) (comp :S :pgname)))
     (into {}))

noisesmith20:05:18

or even (into {} (map (juxt kfn vfn)) items)

matt.kipps20:05:26

out of curiosity, does that provide a benefit if there’s only one map?

noisesmith20:05:22

yes, it avoids creating a lazy-seq nobody needs

noisesmith20:05:16

clojure doesn't do this collapsing of intermediary values automatically (many other languages do), but we can do it explicitly

kari.marttila20:05:19

Interesting. I need to try that tomorrow (it's midnight here in Finland).

kari.marttila20:05:55

How do I learn to see things like "it avoids creating a lazy-seq nobody needs"?

matt.kipps20:05:16

specifically, reading about transducers (https://clojure.org/reference/transducers)

kari.marttila20:05:27

You just looked at the code and immediately saw it?

matt.kipps20:05:55

i personally wouldn’t worry too much about it if you’re learning clojure

matt.kipps20:05:26

transducers can help performance for multi-step list processing

kari.marttila20:05:21

Ok. I read it tomorrow. I think I'm not absolute beginner, I have used Clojure a bit but now want to be more fluent and learn more idiomatic Clojure coding...

lilactown20:05:52

over time you’ll learn what the code actually does: (map f coll) creates a lazy sequence, whereas (map f) does not. so if you’re going to do multiple steps (e.g. map then filter then into), you can avoid creating those intermediate sequence structures and use the more advanced transducer form in order to make it faster

lilactown20:05:24

I only really started to learn this stuff after I started digging into how I can make my horribly slow code, less so 😛 but 90% of the time it doesn’t matter