Fork me on GitHub
Patrick vd Glind19:03:21

Good day everyone

Patrick vd Glind19:03:39

Could someone help me with why having an nREPL is so powerful?

Patrick vd Glind19:03:16

and why other programming languages don’t have it

Patrick vd Glind19:03:54

from the interwebs I can see it allows you to connect to it with a client (VSCode in my case) and remote execute code

Lennart Buit19:03:52

yeah, its an instant feedback loop, I send code to my repl to “test” it the entire day

Patrick vd Glind19:03:29

thank you Lennart


@naxels I think first, Separate the concept of having a REPL from the concept of having a Network REPL Having a REPL is like having a workshop, you use tools in real time as you build, so as Lennart said - you get an instant feedback loop while you are constructing, rather than making changes, compiling etc NREPL is actually useful in many contexts, from editors to production code, anywhere you might want to send snippets of clojure to a remote REPL session. Sometimes the remote is just on your local machine and your text editor integration uses it to improve your dev experience. Sometimes the remote is a running instance of your production software and you want to issue control commands, inspect your software as it runs, or even hot swap code.

❤️ 4
Patrick vd Glind19:03:53

thank you Cameron

👍 4
Patrick vd Glind19:03:33

in learning Clojure, I use the REPL a lot, much much more than I did in Ruby/Go


locally, it provides a much faster feedback cycle, and in prod, it's an invaluable debugging tool

👍 4
Patrick vd Glind19:03:02

now that I use VSCode for Clojure, I was curious what the value of the nREPL was

Patrick vd Glind19:03:15

that is answered, i just need to learn how to work with it ha


Yeah, if you are just using the command line REPL, you have not experienced the real power yet


it's a full on development lifestyle with editor integration (which tends to use nrepl)


for instance, Calva for vscode can reload your file every time you save it



Patrick vd Glind19:03:33

i actually installed Calva

Patrick vd Glind19:03:41

and am curious how the reload your file works


it's a setting

Patrick vd Glind19:03:48

i did find the commands to Evaluate


you have to set it up in preferences > extensions > calva I think

Patrick vd Glind19:03:22

i think it’s called Eval on Save?

Patrick vd Glind19:03:31

(it’s on by default for me)


yeah that's it


so basically you need to have a repl started


in the terminal

Patrick vd Glind20:03:11

lots to learn ha


yeah, it takes time


but when it all clicks you'll live a better life


you need to make sure you have a repl up


are you using lein?

Patrick vd Glind20:03:47

yes i’m using lein and am connected to the nREPL


oh ok calva is connected?

Patrick vd Glind20:03:07

i get the feeling this is one of those things that ones you figured it out you never want to go back ha


in that case open a file, and use (in-ns '<your-namespace>)


in the repl


then you should be able to just define a var


(def whatever 1)


then in the calva repl terminal


eval whatever


I'm paraphrasing here too, I'd have to run through it all again to get it exactly right I'm sure 🙃

Patrick vd Glind20:03:22

do you know of a quick tutorial to get the hang of it?

Patrick vd Glind20:03:32

i tried the above, however doesn’t work for me

Patrick vd Glind20:03:56

(unable to resolve symbol error)


sometimes it's hard to figure out what is wrong with calva


try ctrl + a


ctrl + alt + v and e


to just load it all


and see if it outputs anything red


and you have to check the output terminal called 'Calva Says' sometimes too

Patrick vd Glind20:03:36

aaah i now also see that VSCode had already started a separate terminal for nREPL automatically labelled “Clojure REPL”

Patrick vd Glind20:03:50

i get errors on the namespace


yeah the Clojure REPL one is the calva repl connected to the nrepl

Patrick vd Glind20:03:40

i don’t see any message/output when i evaluate the file


is it green

Patrick vd Glind20:03:53

have a couple of println’s in tehre

Patrick vd Glind20:03:58

yes, the nREPL is green

Patrick vd Glind20:03:07

err orange/yellowish


you can paste a snippet here if you want

Lennart Buit20:03:13

haha, thats surprisingly dutch, you may be interested in #clojure-nl

Lennart Buit20:03:46

oh… already there

Patrick vd Glind20:03:54

haha yeah just found/joined the channel, are you Dutch?

Lennart Buit20:03:21

name shows it a bit, I guess


I thought you may have had a file open already


in a project

Patrick vd Glind20:03:10

i had a file open, but not in a project

Patrick vd Glind20:03:48

i found a free Clojure Udemy course i started to follow

Patrick vd Glind20:03:55

it didn’t mention anything about a project yet

Patrick vd Glind20:03:04

but i got VSCode & Calva setup

Lennart Buit20:03:06

read clojure for the brave and true!

Patrick vd Glind20:03:19

yeah i found that one

Patrick vd Glind20:03:56

thanks for your help guys! lot’s of learning ahead of me

Patrick vd Glind20:03:59

bought a couple of books

Patrick vd Glind20:03:04

want to learn Functional Programming


are you aware of Rich Hickey's talks?

Patrick vd Glind20:03:34

yes, watched quite some of those

Patrick vd Glind20:03:43

that’s one of the key reasons i came to Clojure out of the available languages

Patrick vd Glind20:03:59

like someone mentioned in a blog post, Rich Hickey is a ‘feature’ ha

🙂 4
😆 4
rich 4

I wrote these functions, then realized: all data I'll apply artifacts to will have :results data, but not necessarily :screenshots or :logs. How can I rewrite this so that those values will be nil instead of throwing errors when I try and listFiles of nil?

(def ffilter (comp first filter))

  (defn artifacts
    (let [contents (.listFiles run-on-disk)
          result? (fn [f] (= "junitresults.xml" (.getName f)))
          screenshots? (fn [f] (= "screenshots" (.getName f)))
          logs? (fn [f] (= "logs" (.getName f)))]
      {:results (ffilter result? contents)
       :screenshots (.listFiles (ffilter screenshots? contents))
       :logs (.listFiles (ffilter logs? contents))}))


(and open to other improvements! this is a lot better than what it's replacing but could be better)


:thinking_face: I think I'm using ffilter in the wrong place, for one.... maybe I'll figure this out if I stare at it long enough


is if-let called for maybe?

Lennart Buit20:03:33

having nil keys in a map is not very idiomatic

Lennart Buit20:03:47

its more idiomatic to just omit keys that are nil

Lennart Buit20:03:14

in that case, take a look at cond->

Lennart Buit20:03:18

also, while you are at it, there is letfn to define locally scoped functions


this does seem closer:

(defn artifacts-2
    (let [contents (.listFiles run-on-disk)
          result? (fn [f] (= "junitresults.xml" (.getName f)))
          screenshots? (fn [f] (= "screenshots" (.getName f)))
          logs? (fn [f] (= "logs" (.getName f)))]
      (cond-> {:results (ffilter result? contents)}
        (ffilter screenshots? contents) (assoc :screenshots (.listFiles (ffilter screenshots? contents))))))


(group-by #(.getName %) contents)


well I'm very happy to have finally made a cond-> work with

(defn artifacts-2
    (letfn [(results? [f] (= "junitresults.xml" (.getName f)))
            (screenshots? [f] (= "screenshots" (.getName f)))
            (logs? [f] (= "logs" (.getName f)))]
      (let [contents (.listFiles run-on-disk)]
        (cond-> {}
          (ffilter results? contents) (assoc :results (ffilter results? contents))
          (ffilter logs? contents) (assoc :logs (.listFiles (ffilter logs? contents)))
          (ffilter screenshots? contents) (assoc :screenshots (.listFiles (ffilter screenshots? contents)))))))


but yes, group-by --- forgot all about it

Lennart Buit20:03:45

at least you learned something new along the way!


Hi everyone, I'm writing a function that takes a map and parses it into a sequence of api-calls (which are to be called later). I have something like this currently:

(defn parse-blueprint
  (when (check-keys blueprint)
    (let [[_ api-calls] (-> [blueprint []]
      (when-not (empty? api-calls)
The extract-x functions take in a map and a vector and returns the unmodified map and vector with new stuff appended. I'm wondering what the correct/idomatic way to do something like this would be?


@naxels pardon if someone mentioned already, but a subtle key to why clojure's repl is so useful is that the language itself is designed so that iterated development in a repl can redefine things in a "flow" friendly way


many languages have some sort of repl, with many of them the rules in the repl are subtly different from what works in source files, or there are key things you can't pull apart / examine / redefine in a repl


clojure isn't perfect on this, but far ahead of most compiled languages


hm, I'm not quite there yet --- this seems nice for the cases that have all the results, but I'm NPEing in the case where there's only :results---

(defn artifacts-4
    (let [contents (.listFiles run-on-disk)]
      (cond-> (group-by #(.getName %) contents)
        "junitresults.xml" (update "junitresults.xml" first)
        "logs" (update "logs" (fn [part] (.listFiles (first part))))
        "screenshots" (update "screenshots" (fn [part] (.listFiles (first part))))
        true (rename-keys {"logs" :logs
                           "screenshots" :screenshots
                           "junitresults.xml" :results}))))




you need something in (fn [part] ...) that deals with (first part) being nil


huh. I thought that was covered by the first half of the line. I thought the update would happen if and only if there was something under that key


maybe i'm holding cond-> wrong still? first time~


hmm... you could be right


ooooh I was thinking those keys would act like functions, but I probably have to do (fn [m] (m "screenshots"))


current hypothesis: cond-> is only threading thru the 'action' half of the lines, not the 'assertion' halves


is there a threading macro that hands the passed value to the condition as well as the action?


(alas I gotta put this down for now but thanks for the direction)


there's a version of cond-> that passes changes to the conditions from some library, can't recall precisely at the moment


If I just bind that collection of partitions...

(defn artifacts-5
    (let [contents (.listFiles run-on-disk)
          m (group-by #(.getName %) contents)]
      (cond-> m
        (m "junitresults.xml") (update "junitresults.xml" first)
        (m "logs") (update "logs" (fn [part] (.listFiles (first part))))
        (m "screenshots") (update "screenshots" (fn [part] (.listFiles (first part))))
        true (rename-keys {"logs" :logs
                           "screenshots" :screenshots
                           "junitresults.xml" :results}))))


I think there's still improvements but this looks like it fulfills the requirements


ahh, the key "junitresults.xml" is always truthy


that's your issue


so yeah, looking it up in m fixes it