Fork me on GitHub
#beginners
<
2018-12-18
>
dylandrake04:12:26

Could anyone tell me why this local nested atom is not updating the (cond)?

(defn nested-component
  []
  (fn []
    (let [parent-state (r/atom :parent)]
      [:div @parent-state
        (let [child-state (r/atom :child)]
          (cond
             (= @child-state :child)
             [:div {:on-click #(reset! child-state :updated)} 
               "default behavior"]
             (= @child-state :updated)
             [:div "updated"]))]))
I made it generic to shorten it up. I've been staring at this for hours and can't figure out why the second condition is not rendering once the default div is clicked. I have confirmed the atom is actually being updated but the [:div "updated"] is not rendering. EDIT: I think when child-state is updated the entire parent component re-renders and child-state is set back to :child

adc1704:12:00

Hi 👋 , It looks like the clojure community is into building code as small, easily composable pieces of architecture. This requires me to think harder and more critically about the architecture of my apps, which is great, I want to build that muscle! Where I could really use some help is learning a solid process for debugging the errors that show up when I try and compose things together. e.g. A small one: I just ran boot repl in a new luminus +boot project with clojure 1.10, java8, boot 2.8.2 and got this error:

java.lang.Exception: No namespace: reply.eval-modes.nrepl found
How would people approach debugging this step-by-step? I want to learn how to solve these kinds of problems myself so I can handle myself better in the clojure ecosystem.

seancorfield06:12:33

Alex's advice is good for debugging things once you have a REPL up and running. Unfortunately you are running into a problem caused by conflicting versions of tooling. You'll probably need to use a different version of Boot to resolve that. I'm not at my computer right now but, if no one has helped you solve this by then, I'll try tomorrow.

seancorfield17:12:55

If you downgrade to Boot 2.7.2, that warning will go away:

(! 538)-> BOOT_VERSION=2.8.2 boot repl
nREPL server started on port 61847 on host 127.0.0.1 - 
java.lang.Exception: No namespace: reply.eval-modes.nrepl found
REPL-y 0.4.1, nREPL 0.5.3
Clojure 1.10.0-beta8
OpenJDK 64-Bit Server VM 1.8.0_192-b12
...
Tue Dec 18 09:17:18
(sean)-(jobs:0)-(~/clojure/adc)
(! 539)-> ^2.8.2^2.7.2
BOOT_VERSION=2.7.2 boot repl
nREPL server started on port 61872 on host 127.0.0.1 - 
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.10.0-beta8
OpenJDK 64-Bit Server VM 1.8.0_192-b12
This is due to a transition in tooling, where nREPL moved from a Contrib library to a community library and a bunch of namespaces changing as part of that, as well as a number of tooling version upgrades that have been happening. At some point there should be a new version of Boot that is fully compatible with the new nREPL tooling.

adc1702:12:42

thanks @seancorfield,how would I have figured that out myself? as a beginner to clojure, the tooling is often an obstacle. java + leiningen + boot + managing parens (+ a bunch of other complicated stuff when working with clojurescript) is pretty challenging. any advice on staying on top of this kind of issue?

seancorfield03:12:27

This sort of thing is hard and, luckily, fairly rare. I actually think it's easier to start with the clj CLI and deps.edn file approach -- it's directly supported by the Clojure/core team and it's well-documented on http://clojure.org

seancorfield03:12:59

That said, you'll run across Leiningen pretty much everywhere because it's the oldest tool and it's in all the books (and most of the tutorials).

seancorfield03:12:34

Boot hasn't been a model of stability lately, unfortunately, but it was pretty awesome from around 2.5 up to the 2.7 release. Only 2.8 has been flaky.

seancorfield03:12:37

At work, we started with Leiningen way back in 2011, migrated to Boot at the end of 2015 to get more flexibility, and recently migrated to the newer, simpler tooling provided by Clojure itself. We're glad to be away from the big, complex build tools but right now there's no version of clj for Windows (so I use Windows Subsystem for Linux for my Clojure work on Win10).

adc1712:12:04

thanks, will read up on the clj tooling

alexmiller05:12:38

Walk through each expression and eval it to a repl

alexmiller05:12:02

Stu Halloway has a great debugging talk on YouTube

Casey11:12:14

i'm looking for a function to merge a vector of kvs into a map .. (?? {:a -1} [:a 1 :b 2 :c 3]) => {:a 1 :b 2 :c 3}

yuhan11:12:16

apply assoc?

Casey11:12:24

i thought hash-map could help, but i'm not sure how

Casey11:12:12

yea! so simple

gon11:12:00

(merge {:a -1} (apply hash-map [:a 1 :b 2 :c 3]))

Audrius12:12:42

what this one do:

(defn index-of [in value]
  (loop [in in
         i 0]
    (if (= (first in) value)
      i
      (recur (rest in)
             (inc i)))))

gon13:12:36

it recurs on the collection in and returns the index position of value in the collection (if found)

enforser15:12:10

Does anybody have a good idea on how to ensure a child thread invoked in Clojure dies when it's parent dies? My current solution is like:

(let [main-thread (Thread/currentThread)]
  (future 
    (loop []
      (when (.isAlive main-thread)
        (recur)))))
which works for a some non-zero amount of time, but the child thread seems to die randomly before the calling thread is closed. Not too sure why this is happening 😕

valerauko15:12:13

luminus server start has a bit like this (.addShutdownHook (Runtime/getRuntime) (Thread. ^Runnable stop-server)) (stop-server being an fn) maybe that's what you're looking for?

valerauko15:12:03

there's also shutdown-agents but i'm not sure what that does

enforser15:12:54

yeah, I think I could potentially use this to set the status of the main thread to closed. It might be more reliable - will try it out! Thanks

alexmiller15:12:26

in the JVM, if you mark a Thread as a daemon thread, it will not prevent the JVM from shutting down if it has not finished

alexmiller15:12:46

future uses thread pools where the threads are NOT daemon threads, but you can make your own thread instead:

alexmiller15:12:35

(doto (Thread. ^Runnable #(println "whatever")) (.setDaemon true) (.start))

enforser15:12:22

In my case I want the child thread to continue running dependent on other cases. If either the parent thread is running or an atom containing a list that it is referencing is not empty, then the child should carry on. I was using claypoole to get the daemon thread, but ran into the issue of potentially losing the items on the queue when the calling thread is closed I'll make a better example, give me a second

enforser15:12:21

I'm doing something like this:

(let [queue (atom (clojure.lang.PersistentQueue/EMPTY))]
  (future
    (loop []
      (Thread/sleep 2000)
      ;; processes events on queues here, before going to next s-expr
      (when (or (parent-thread-lives)
                (not (.isEmpty @queue)))
        (recur))))
  queue)
parent-thread-lives is the part I'm not sure how to get right

alexmiller15:12:33

well, the isAlive check mentioned above is probably the shortest step (just pass in a handle to the producer thread)

alexmiller15:12:56

you may also want to just use agents, which have about a 90% match to this too

enforser15:12:50

for some reason, the child thread dies before the main thread when I use .isAlive. If I remove the check, then the child thread will run forever - so I'm fairly confident it's not a different issue with the operations occurring in the child thread. Oh well, I'll keep trying to debug since it sounds like I'm on the right track of how to do this! Thanks!

hiredman17:12:58

I would be very suspicious of using isAlive

hiredman17:12:13

my guess is you are using nrepl?

hiredman17:12:16

I forget the details but nrepl with evalute code in a different thread for every eval message sent to it

hiredman17:12:45

so (Thread/currentThread) will return some random threadpool thread that may just go away

enforser17:12:58

I am not using nrepl in this case, but it does seem to me like isAlive isn't working as I expect. I'm still getting the occassional false from isAlive, which kills the child process when it isn't supposed to.

RodgerDodger17:12:14

Hello all. I am using the following macro from clojuredocs (with-out-str entry) for passing the printed time result and its body from a time function (ex. (time (+ 1 2)) into other functions. )

RodgerDodger17:12:02

Now everything is working, but I am confused about the (binding [out s#] portion of the code

RodgerDodger17:12:10

My initial thinking was that it would be (binding [s# out], since s# is the value given to the :str key.

RodgerDodger17:12:59

How is the value of "Elapsed time 123.21313ms" being bound to s#?

dpsutton17:12:25

if you check out the source of pr you will see it uses the dynamic var *out*. This is why dynamic vars are useful. You substitute that var with a string writer, let it accumulate the writes and then return it at the end

dpsutton17:12:09

out is normally system.out or whatever in java. and you want to redirect things from that to your s# var which is a java io StringWriter

RodgerDodger17:12:25

ahh, that makes sense! Thank you @dpsutton

jstaab23:12:16

Is there a common idiom for mixing regular function application with apply? What I usually find myself doing is something like (apply dissoc (concat [m] ks)) which seems a bit weird and contrived

jstaab23:12:50

Another alternative might be (apply (partial dissoc m) ks)

jstaab23:12:03

But that's also odd and not terribly readable

seancorfield23:12:55

(apply dissoc m ks) should work

jstaab23:12:35

gotta remember to look at the docs because there's always that extra little touch to the standard library that other languages lack

seancorfield23:12:41

With apply, only the last argument needs to be a collection. All the others can be provided directly: (apply f arg1 arg2 arg3 args)

jstaab23:12:05

That's just completely intellligent

Eccentric J23:12:47

Ah Ramda, my first functional programming ❤️

jstaab23:12:34

Best gateway drug ever

jaihindhreddy05:12:23

@U1KLLEBSA Me too! Underscore->Ramda->Clojure(Script). I thought Ramda was elegant. Many functions in clojure.core have multiple arities that really make sense.

jstaab23:12:05

Thanks @seancorfield!