This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-07-01
Channels
- # beginners (10)
- # cljs-dev (33)
- # cljsjs (4)
- # cljsrn (12)
- # clojure (39)
- # clojure-belgium (2)
- # clojure-russia (80)
- # clojure-spec (9)
- # clojure-uk (6)
- # clojurescript (22)
- # core-async (141)
- # cursive (2)
- # datomic (20)
- # devops (1)
- # emacs (20)
- # hoplon (1)
- # jobs (3)
- # lambdaisland (12)
- # leiningen (3)
- # lumo (44)
- # onyx (2)
- # pedestal (1)
- # quil (1)
- # re-frame (9)
- # reagent (4)
- # robots (1)
- # rum (3)
- # spacemacs (5)
- # uncomplicate (80)
- # untangled (46)
- # yada (2)
I have been exploring core.async And tried to implement : gophers daisy chain whispering from here https://www.youtube.com/watch?v=f6kdp27TYZs&t=1600
doseq returns nil
oh wait your println is in a weird place, never mind
putting gopher-talk inside go is pointless
all it does is block a go block - for zero benefit
no, use put!, that's what it's for
this isn't the cause of your problem, it's just a random problem with the code
not sure yet,still reading
you could be doing that yes, because you are doing blocking ops inside go
@diginomad can you back up for a moment and describe the problem statement? like, what's supposed to happen? I see the youtube presentation but I don't know the go
language. describe what should happen
@diginomad yeah, if N is high enough that code is guaranteed to deadlock
that's what you expect to happen, but that's not describing what should happen in order to produce that result. more detail on the process?
i think i see now -- but maybe it will help you to talk it out too, so go ahead ๐
so, each {left <- right;right = left ; left = new channel} is supposed to be in a new "thread"
@diginomad I added one more println, it stalls on the blocking read inside the println at the end
the channel is empty so it blocks
I just checked, after thedoseq is done both right and left are empty
I wouldnt' say surely anything about this code, it's weird
but why right,as i am resetting it to a channel which just had some stuff put in by gopher-talk
because it has to read another channel, and you are doing channel reads of both channels before writing to either
there's no code path that writes without reading first, so it's a deadlock
since nobody can possibly read
wait... no, right should be readable...
@diginomad OK I added a println to gopher-talk - the doseq is exiting before gopher talk finishes its channel write
yes, you can
but don't call functions that use >!! inside a go block
i have a solution, if you'd like to see it @diginomad
i tried to go from what you have, but the atoms and structure threw me off so i just started it from scratch
yes, any time atoms are created local within a function it's a strange thing to see
it could be a lot better but it's an initial 10-minute solution ๐
(defn gopher-talk [right left]
(go
(>! left (inc (<! right)))))
(defn gophers-daisy-chain [n]
(let [leftmost (chan 1)
rightmost (chan 1)]
(put! rightmost 0)
(loop [right rightmost
left (chan 1)
i 0]
(if (= i n)
(gopher-talk right leftmost)
(do
(gopher-talk right left)
(recur left
(chan 1)
(inc i)))))
(<!! leftmost)))
@joshjones - nice translation
@diginomad generally we prefer to substitute mutation with recursion setting new immutable bindings
both function arguments and let bindings are immutable
(and loop bindings)
@diginomad (recur new-x new-y) in a loop is equivalent to resetting x and y
notice that @joshjones also replaced your blocking channel ops with parking ones
yes @noisesmith -- actually, if you change the parking puts and takes in gopher-talk
with blocking ones, it does not work
right, >!! is not meant for use in go blocks, >! is
almost @diginomad -- it allows >!!
in a go
block, but does not allow >!
outside a go
block
@diginomad also you can change all your calls to chan
in your original with (chan 1)
and that makes the original work
core.async chans are nonbuffered by default, and that code stalls without buffering
but use loop and rebinding and >! inside go blocks like in @joshjones example anyway, that's the right way to do it in clojure
it's just weird
better to avoid if possible (though not always possible, it usually is)
@diginomad if you take your original example and make gopher-talk
contain the go
block, and change the >!!
to >!
and the <!!
to <!
, you get interesting results --
(gophers-daisy-chain 50)
45
=> nil
(gophers-daisy-chain 50)
47
=> nil
(gophers-daisy-chain 50)
50
so, a nice little race conditionatoms are for stateful things that are modified from more than one thread, usually
@diginomad I can't say I've ever seen an atom in production clojure code in a let
binding -- would definitely say it's bad practice
yeah, atoms and laziness are a bad mix
once you shift your thinking from variable assignment to evaluation of expressions, and buy the whole "values are values, and they don't change" approach, you have to really try to find opportunities to use atoms -- they just don't come up that often
and your race condition as shown above is a perfect example of why mutable state is just an impediment to everyday development. problems are hard enough to solve without involving state
it's a (relatively) simple matter of looking at idiomatic code and beginning to model those best practices in your own code, and writing it often enough for it to become habit
I don't actually know what hackerrank problems look like -- if only your solution matters, or ...?
for exercises, there's http://4clojure.com where you can see other people's solutions
thanks for the suggestion @noisesmith
thanks guys @joshjones @noisesmith
I think hereโs the same thing (daisy chain) in core.async and manifold - https://github.com/dm3/manifold-cljs/blob/master/examples/src/manifold_test/daisy.cljs
@diginomad I just rewrote this, the way I initially wanted to. much shorter, and more or less clear, depending on your familiarity with functions like partition
(defn gopher-talk [right left]
(go (>! left (inc (<! right)))))
(defn gophers-daisy-chain [n]
(let [rightmost (chan 1)
leftmost (chan 1)
chans (concat [rightmost] (repeatedly n #(chan 1)) [leftmost])
chan-pairs (partition 2 1 chans)]
(run! (fn [[r l]] (gopher-talk r l)) chan-pairs)
(put! rightmost 0)
(<!! leftmost)))
I wouldn't say it's more idiomatic necessarily, but it does avoid the explicit loops, and the idea is more simple: create pairs of side-by-side channels, link them up, and you're done
there's not as much of the notion of "the previous right gopher is now the left gopher" -- just thinks of them in pairs
it's map without the return value or the alternate arities
(or the laziness)
it's relatively new (1.7) so it's not as common but it should be used in place of (doall (map ...
in cases like this
the real alternative is dorun on map
but yes, run! is better ๐
pretty much yeah
have a good afternoon/evening/morning all, i'm off to enjoy the sunshine while it lasts here ๐
need to call shutdown-agents
in your -main
function. explained here: https://clojure.org/guides/faq#agent_shutdown @diginomad
@diginomad FWIW future
s will fail for large n
, not a good use case here. you were probably just trying things, but wanted to mention