This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-12-10
Channels
- # adventofcode (54)
- # announcements (30)
- # asami (13)
- # aws (10)
- # babashka (16)
- # babashka-sci-dev (44)
- # beginners (95)
- # calva (63)
- # clara (10)
- # clj-kondo (3)
- # cljfx (6)
- # cljs-dev (7)
- # cljsrn (1)
- # clojure (68)
- # clojure-europe (59)
- # clojure-nl (7)
- # clojure-norway (12)
- # clojure-spec (6)
- # clojure-uk (6)
- # clojurescript (4)
- # component (4)
- # conjure (5)
- # datomic (3)
- # deps-new (1)
- # events (4)
- # exercism (1)
- # figwheel-main (1)
- # fulcro (33)
- # gratitude (1)
- # improve-getting-started (3)
- # jobs (3)
- # lsp (5)
- # malli (10)
- # membrane (5)
- # music (3)
- # nextjournal (6)
- # off-topic (42)
- # pedestal (2)
- # polylith (14)
- # portal (11)
- # re-frame (42)
- # releases (3)
- # reveal (4)
- # shadow-cljs (62)
- # tools-build (1)
- # tools-deps (3)
- # web-security (1)
- # xtdb (3)
Besides s-exprs (which I'm not sure if they really matter; but definitely want to avoid for this thread), what feature doe the Clojure REPL have that the Scala/Groovy REPL lacks that allows Clojure REPL-driven-development to go more nicely than Scala/Groovy REPL-driven-development ?
> Besides s-exprs (which I'm not sure if they really matter; but definitely want to avoid for this thread)
They certainly matter. Without s-expressions, it is much more difficult to determine what to send from the editor to the REPL, and therefore evaluate individual pieces of code.
Besides that:
• Clojure allows (and encourages) you to develop entire programs at the REPL. In other words, there are no limitations on what you can do at the REPL. Clojuer does not require you to have any code in a file in the file system.
• Scala and Groovy REPLs are not REPLs. They're prompts, or shells, or something else, not sure. A REPL is composed of individual read, eval, print, and loop steps. Scala and Groovy do not have a read step, for instance. See https://groups.google.com/g/clojure-dev/c/Dl3Stw5iRVA/m/IHoVWiJz5UIJ for more. Moreover, with Scala and Groovy, it is not possible to start a nested REPL for some other purpose (see things like https://github.com/eggsyntax/datawalk or https://github.com/dpsutton/grepl for examples).
• Scala and Groovy do not have (to my knowledge, at least) a tangible runtime. Clojure offers a multitude of ways to inspect (e.g. clojure.repl/apropos
etc.) and modify (e.g. by redefining functions) the runtime.
• It is not (to my knowledge) possible to specify a Java system property that serves a Scala/Groovy REPL over a socket connection which you can then connect to debug (and if you're brave, modify) code running in a production or staging environment.
Just the first couple of things that came to mind.
I'm not intimately familiar with Scala or Groovy REPLs, of course, so I'm happy to be corrected on any of the above.
They are repls, they're just much less useful. The way lisps work, redefinitions dynamically influence the entire program
If in Scala you have a class whose behavior changed, the previous instances living in your REPL are not updated, then you need to rebuild your state
@UK0810AQ2: Are you referring to https://clojure.org/reference/vars or something else ?
@U3JURM9B6 vars enable that by introducing a level of indirection
Instead of directly calling the invoke method on the function object, you first dynamically deref the var. You can eliminate this by turning on direct linking
Anyone here using https://github.com/gregsh/Clojure-Kit instead of Cursive? (Mainly interested in the open source nature + integration with IDE scripting console.)
I have the following:
(loop [keys* (keys result) db* db]
(if-not (first keys*)
db*
(recur
(rest keys*)
(assoc-in db* [:datasets (:id result) (first keys*)] ((first keys*) result)))))
But not sure why I’m getting this error:
null
Can't recur here at line 493 projectgun/events.cljs
it works for me
(def result {:id 100 :a 1 :b 2})
(def db {})
(loop [keys* (keys result) db* db]
(if-not (first keys*)
db*
(recur
(rest keys*)
(assoc-in db* [:datasets (:id result) (first keys*)] ((first keys*) result))))) => {:datasets {100 {:id 100, :a 1, :b 2}}}
ah but I did this in clojure , not cljs
How horrible is this if I want swap polymorphic on ref type?
(defn- swapper
"Return swapper that executes f exactly once, preferably atomically"
[ref]
(condp instance? ref
Atom (fn [f & args]
(let [tried (atom false)]
(swap! ref (fn [v]
(if (compare-and-set! tried false true)
(apply f v args)
v)))))
Agent #(apply send ref %&)
Ref (fn [f & args]
(let [tried (atom false)]
(dosync
(alter ref (fn [v]
(if (compare-and-set! tried false true)
(apply f v args)
v))))))
Var #(apply alter-var-root ref %&)))
fwiw ref is a clojure core reserved word/symobol, would avoid re-using it, to avoid confusion.
As an example i copied in some code to dissect what was going on, and was very confused. Imagine trying to read a giant code block, of course you hope they can quickly find the source, but still it forces you out of the context you hoped to stay in.
the Atom one looks suspicious to me. I can see how it would execute f once but the result is thrown away. Not sure what guarantees you want to make on f’s return value.
This looks a lot like the problem recently dealt with here: https://www.youtube.com/watch?v=IsS8ZCSUTUQ
there’s also a subtle issue where you need to determine your semantics. Are you ensuring f
is only called once per call site or f
is called only once per value in the atom. If its the former, this is basically just a “deref, and reset!” with that explicit race value as you just keep clobbering. Otherwise it is very close to just a regular atom
Good points, thanks yall! I think in my case retries should throw. The reason is I'm making a UI for Reveal (live view) that watches any type of ref and has controls to update it
basically integrant/component/mount live view, where systems typically live in atoms or vars
I'm ensuring 1 call per call site since calls are side-effecting and non-retriable - starts and stops of the system
I might be reading it wrong but it reminds me of some Python code I had to debug a few years ago that extensively used properties where getters were mutating things.
you have no reason to cas, and start/stop of systems in an atom via swap! explicitly violates the contract of swap
this is not threadsafe, and cannot be made threadsafe, to clutching to constructs like cas is silly
it will because the exception will contain started system that user will be able to stop manually
I think it might be useful to have a little more context on the use case. • what's a story of the typical usage? • how many refs are usually involved? Always one, or varying numbers? • what's the story for usage when a "swap" fails?
Well yes, it is a property of swapper. I don't want to do just deref+reset because if a system was started by some other code between deref and reset I might have 2 systems running and both might need cleanup. Just doing a reset will leave prev system running, but it will be lost. CAS will keep that system running and will return extra system to the user so they can deal with it.
the only way you are going to make a pile of mutable state and side effects thread safe is a big lock around it, which is basically what you are doing with your extra atoms
and once you have a lock around it, mutating it using clojure's reference model, why bother?
I cannot speak for integrant/mount specifically, but I use component a lot and can't recall ever putting the system in a ref, agent, or atom
usually there main function of the server starts the system and sets the var, the var is just there for repl reference and is never mutated
I imagine people put component systems in atoms because the idea of mutating a var at system startup makes them squeamish, and "oh, if I am mutating something, it must go in an atom"

Hate is nether helpful. Be nice to each other ☮️
but maybe I'm reading into things
it’s what happens when we all, as a community, have to go deal with java logging /s
btw I don't hate mount, I use it at work now and it's more convenient than integrant (I have experience with both)
The real irony is this is the comment that’s ignoring all context
The component-like libs always seem to be the flame wars in Clojure haha. But in my experience, like all good flame wars, they're all a pretty good choice actually, and that's why it creates flame wars. Someone once said, when two senior engineer argue loudly, it means both their ideas are pretty damn good, so much so that they can't agree which one is better.