Fork me on GitHub

Hi there. I'm not sure if this question belongs here but I can't find another proper place for it. I'm working on a CLI Clojure app which has a hot-reload feature for certain files it's working with. I'm using and it seems that every time I change a file there are two events fired instead of one. Looking around on StackOverflow it seems that Java's WatchService is fired twice when the content is changed and when the modification timestamp is changed. Has anyone encountered anything like this in their work? Doing double work is not really a biggie but def something I'd like to avoid. Thanks in advance.


It seems that only Windows is throwing double events and that's by design?


Anyway found a Java library to handle this for me.

Claudio Ferreira13:02:12

Understand deeply OOP is a pre-requisite to understand and code proficiently in funcional paradigm? A beginner that is gonna work with functional languages HAS to learn deeply OOP?


I do not believe someone must deeply learn OOP as a prerequisite to learn Clojure.


It is useful to know a tiny bit about Java APIs, for times when you want to use a Java library, but that is nothing like "deeply learning OOP"


I'd argue java isn't even really oop anyway, or at least the java / c# code bases I've seen aren't. Its mostly just imperative code wrapped in classes.

Tim Robinson16:02:47

I've read that those with no OOP background can find it easier to pick up functional programming, but I can't speak from personal experience. as Andy says though, familiarity with Java would be a benefit for clojure


I have heard people who have done years of Java and object-oriented programming say that they felt when learning Clojure that it took time for them to "unlearn" some habits they had from object-oriented languages, and learn more functional/immutable-data-structure styles of doing things.


(Not really "unlearn" -- just learn different/new patterns and habits.)


easy/dumb question: How can I break out of an infinite loop in the repl?

Claudio Ferreira11:02:22

Just click that red button when u are evaluating: Did i answered your question?


with Emacs & Cider: C-c C-b

Tim Robinson16:02:02

hmm i had that problem just yesterday and had to kill the java process


Yep, that’s annoying. I’d like to be able to gracefully shutdown via signal or ctrl c


Some repls offer a way to attempt this. What’s your repl setup?


Some REPLs you can type Ctrl-C and it will kill the whole JVM process. There is a stop() JVM API method that can be used to kill a thread, but it is deprecated because in the general case it can lead to a state of your JVM where locks are held and never released on objects, and leave them in an intermediate bad state. ( for some details and alternatives to using the stop() method, but they require that your loops that might go into an infinite loop to be modified).


in nrepl, control+c kills the current thread's computation, but doesn't kill the process


Using the stop() method probably? i.e. with its warnings about possible bad state being left behind?


Those warnings are less commonly important the way Clojure code is usually written, I believe, but in general if you are calling arbitrary Java libraries in the thread being stopped, it could be wonky.


maybe the fact that they know what state is there (the state made by a REPL thread) means they can use it safely?


the REPL will be in a namespace, killing that thread doesn't change scope of anything in that namespace


the only gotcha would be if something in your looping / stuck code left an object in a bad state, but you'd be unlikely to see that happen with clojure apis themselves


I guess? I'm realizing that's a lot of speculation and I should just read their code.


Yeah, I can't see using stop on a thread in a JVM running Clojure causing corruption of any of your defined functions, nor immutable collections. It is mainly the mutable stuff that you might be manipulating via Java libraries that would be most at risk.


the one gotcha I'm considering is if you left your IO in a bad state, with nrepl you'd be leaving that open if you don't exit the client...


If that killed thread were manipulating a transient collection, it might be left in a bad intermediate state, but most likely the fact that the thread was killed means probably no other thread has a reference to the transient, anyway.


right - transients have a big "only use from one thread" warning on them :biohazard_sign:


I’m playing around with Clojure by doing deaf aunty, basically it gets input from the user and returns a response depending on if the string is fully capitalised or not. To leave the conversation with “aunty” you need to say “BYE” 3 times. My code is as below

(ns deaf-aunty.core)
(require '[clojure.string :as str])

(defn is-all-capitalized? [string]
  (= string (str/upper-case string)))

(defn talk-to-aunty [string]
  (if (is-all-capitalized? string) " NO, NOT SINCE 1938!" "HUH?! SPEAK UP, SONNY!"))

(def consecutive-byes (atom 0))

(defn deaf-aunty-run []
  (case (read-line)
    "BYE"   (if (= @consecutive-byes 3)
              (println "Okay BYE!")
                (println "What!? You want to leave????")
                (reset! consecutive-byes (+ @consecutive-byes 1))
    (reset! consecutive-byes 0)  
    (println (talk-to-aunty read-line))  (recur)

So my question is with this part
"BYE"   (if (= @consecutive-byes 3)
              (println "Okay BYE!")
                (println "What!? You want to leave????")
                (reset! consecutive-byes (+ @consecutive-byes 1))
The Rubyist in me is itching to extract that out into a function, but not sure how to run recur in the extracted function so that it reruns deaf-aunty-run. Feel free to comment/criticise other parts as well!


you might want to cross post this to #code-reviews for starters, regarding readability, it would help a lot if you followed community conventions (no trailing ) on their own line, always indent children further than parent forms)


Okay! I actually didn’t know that channel existed, thanks!


the normal way would be for the function you run to take a parameter (defn foo [x] ... (recur (f x)) ...)


then the same parameter can be passed to a helper function. also, using an atom means that your code is impossible to use with threads


If you do use an atom, it is idiomatic to use (swap! consecutive-byes inc) instead of the call to reset! you show in your code.


But noisesmith's comments are spot on for how to avoid the use of an atom altogether.


there's a proof somewhere I've misplaced that the usage of a mutable value can always be replaced with a parameter taken by and returned from each function touching it (though this requires some amount of code restructuring to pull off in the general case, here it's trivial)


I see, okay lemme try and rewrite it, thanks for the tips!


Does anyone here know vega[lite]? I am trying to chart some data using oz, and I don’t know how to get it to recognize timestamps as quantifiable values


#data-science may be a good place to ask


what's the recommended way to run a zero arity function with clj? clj -X my.ns/my-fn will give Wrong number of args (1) passed to: my.ns/my-fn


-X only runs functions with a single argument, which must be a hash map.


You could run it with -e instead but you'll need to require/resolve it -e "((requiring-resolve,'my.ns/my-fn))"


I have several zero arity functions that would be nice to be able to invoke from the command line and I don't necessarily want to change them to just to run with -X. I can always make something to put in my deps.edn, but I was just wondering if there was already a recommended approach


you could make a single arity function which accepts information about which zero arity fn to call?

👍 3

requires setting up a single function rather than changing all of your zero arity versions


doesn't sound too hard. I basically just want something like -X with the same parsing rules, but instead of producing a single map to pass to a 1 arity exec function, it produces a vector to produces to apply to an exec function. Just wanted to double check that this functionality didn't already exist.