This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-02-13
Channels
- # bangalore-clj (1)
- # beginners (29)
- # boot (13)
- # braveandtrue (5)
- # clara (5)
- # cljs-dev (42)
- # cljsrn (7)
- # clojure (55)
- # clojure-france (46)
- # clojure-nl (11)
- # clojure-portugal (1)
- # clojure-russia (268)
- # clojure-spec (26)
- # clojure-uk (32)
- # clojurescript (173)
- # clr (2)
- # core-async (46)
- # cursive (22)
- # datomic (33)
- # devcards (1)
- # emacs (5)
- # events (8)
- # figwheel (2)
- # flambo (4)
- # instaparse (8)
- # jobs (11)
- # klipse (46)
- # lein-figwheel (3)
- # london-clojurians (2)
- # nrepl (1)
- # off-topic (29)
- # om (4)
- # om-next (8)
- # pedestal (3)
- # rdf (4)
- # re-frame (51)
- # reagent (104)
- # remote-jobs (1)
- # rum (4)
- # schema (2)
- # specter (19)
- # untangled (16)
- # vim (52)
Hi there!
I’m reading Aphyr's post - https://aphyr.com/posts/306-clojure-from-the-ground-up-state
And have a problem with the exercise #5 (at the bottom of the page). Here is my current solution:
https://gist.github.com/olessavluk/efcd425cda7dc37eff991ba45d34e7c3
I’m getting NullPointerException
with it. But, when I replace future
with delay
it work fine.
Answer is correct in both cases.
Any ideas why do I get NullPointerException
when using future
and do not have it when using delay
?
you are reading @work outside the dosync
transaction so it could be emptied by the time you enter the transaction. In that case, (ensure work)
returns nil
, then first
will return nil, and then +
will throw an NPE - is that what you’re seeing?
Thank you, Alex! You are right about the problem cause
Also wondering is it ok to execute recur
inside the dosync
?
like this:
(defn do-work []
"Take the first element of `work` and add it to `sum`, until all of the `work` is done(empty)"
(dosync
(when-not (empty? @work)
(alter sum + (first (ensure work)))
(alter work rest)
(recur))))
Does that mean that the transaction will be started on the first function call and terminated only after the last call? Does this have some performance problems?olessavluk: there might be a minimal performance overhead. You should be able to move the dosync out of the recursion reasily
@ordnungswidrig I disagree, every time you go into a dosync you have to re-lock all the refs.
but of-course, measure first, then decide
Oh really? I thought it would only check for an existing lock but you’re right, the scope can be different.
hm, interesting.
but how can I move recur
out of dosync
to test this?
The only problem with doing more work inside a dosync (transaction) is that you have to throw out more work in the case of a conflict
but summing a few integers is likely to be a small enough amount of work that you might as well just do it that way
but call to recur mean that there will be a conflict with a 100% probability, isn't it?
I think that totally depends on the kind of work done
no, conflicts happen with other threads, you only have one thread here
this all being said, STM is very rarely used in production Clojure code. 99.99% of the time the better route is a atom:
(atom {:sum 42 :work [1 2 3]})
yes, but this is just an exercise to better understand how the refs works and when to use them or atoms
that's kindof the problem, there isn't really one
understanding them is fine, I'm just saying don't worry if you don't "get" them right away. I've been programming Clojure professionally for almost 5 years, and I've never seen them in use, or at least in a use that required them.
hm, I thought this is one of the coolest things in clojure so most of the time atoms are enough or you don't even need to share the memory for writing?
> no, conflicts happen with other threads, you only have one thread here I’m running this function in two threads:
(defn calc []
"Calculate `sum` using two workers"
(let [a (future (do-work))
b (future (do-work))]
@a @b)
@sum)
(full code - https://clojurians.slack.com/archives/beginners/p1486947230008234)
So in my case I will calculate every transaction two (or even more times) because I have recur which will modify refs inside a dosync? Is that correct?I would guess that one thread would get done first, update sum
and work
, and then the other thread would retry with an empty work
and basically do nothing. Is that what you're seeing?
I feel like the Clojure STM is underrated, but on the other hand I’ve never encountered a situation where an atom didn’t suffice either ¯\(ツ)/¯
Yea, it is underrated, but I see it as a example of how much leverage you can get out of other models, CAS (atoms) + immutable data are just so powerful and simple, they often trump other more complicated methods.
The same applies to CSP (core.async), agents, actors, etc.
Never underestimate the performance you can get out of a single thread when it is properly optimized, and unconstrained cross-thread synchronization.
having some trouble thinking through a java interop issue, would love some input. newish to the jvm so pardon me for working out the kinks in my mental model.
I need to implement an instance that satisfies an interface with a single method. this instance will then be passed to a method that expects a java.lang.Class
.
in an ideal world I’d love to be able to pass the body of the function in at instance creation time, as opposed to specifying it in advance
I initially reached for invoking reify
, but it seems like reify doesn’t actually return a class instance. if there’s a way to cast it to a class that would be ideal.
based on https://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/ I then figured defrecord
would be the ideal function to use.
my q is: can I avoid creating a defrecord for each invocation of a different function body? in a situation like:
(defn make-an-instance [my-func]
(defrecord MyRecord []
my.interface
(interface-method [this]
(my-func))))
I would assume that each invocation of make-an-instance
creates a new MyRecord object that won’t get garbage collected – I hope I’m wrong about that.
would appreciate any insight that anyone can provide