This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-06-28
Channels
- # adventofcode (12)
- # announcements (1)
- # asami (18)
- # aws (1)
- # babashka (20)
- # beginners (80)
- # calva (11)
- # cider (10)
- # circleci (5)
- # cljsrn (2)
- # clojure (37)
- # clojure-australia (15)
- # clojure-europe (32)
- # clojure-nl (2)
- # clojure-uk (26)
- # clojured (7)
- # clojurescript (10)
- # clojurewerkz (3)
- # cursive (45)
- # data-science (12)
- # datomic (3)
- # emacs (8)
- # events (3)
- # fulcro (23)
- # graphql (5)
- # helix (3)
- # jobs (1)
- # jobs-discuss (1)
- # juxt (3)
- # lsp (12)
- # malli (4)
- # meander (4)
- # reagent (11)
- # releases (1)
- # shadow-cljs (28)
- # sql (35)
- # tools-deps (9)
- # xtdb (25)
What is sort of the "standard" way to deal with concurrent data structures with mutable state in clojure? Examples work wonders. So lets take the standard bank account example:
(defprotocol AccountAPI
(withdraw [this amt] "withdraw the given amount from the account")
(deposit [this amt] "deposit the given amount into the account"))
(defrecord Account [name balance]
AccountAPI
(withdraw [this amt] (conj this {:balance (- (:balance this) amt)}))
(deposit [this amt] (conj this {:balance (+ (:balance this) amt)})))
(def account (atom (->Account "foo" 100)))
(require '[clojure.core.async :as async])
(dotimes [_ 5]
(async/thread
(swap! account withdraw 2)
(swap! account deposit 1)))
@account ;; => {:name "foo", :balance 95}
This works nicely as withdraw
and deposit
are side effect free. How would this "normally" be done if withdraw
and deposit
are not side effect free? Let's say for example they do I/O. Can I still avoid explicit locking somehow?I used a combination of deftype
,`locking` and set!
for my use case. The result just doesn't look and feel very clojure like, but more like some awkward translated java hence the question.
@clojurians-slack100 From the docstring of dosync
:
> The exprs may be run more than
> once, but any effects on Refs will be atomic.
That's probably why we keep it free from side effects, like in this example, right? https://clojuredocs.org/clojure.core/dosync#example-542692c7c026201cdc32698d (which is really my primary link to that page 😛)
so don't use side effects in there, unless the side effects may happen multiple times
i read a paper once where they implemented a few different ways to coordinate side effects in a transaction within Clojure’s STM
the one that felt most natural to me was the “defer” method that basically only ran the side effect once it was sure the transaction would commit
Agents already do that: > Agents are integrated with the STM - any dispatches made in a transaction are held until it commits, and are discarded if it is retried or aborted. https://clojure.org/reference/agents
The ants simulation uses this functionality: https://github.com/juliangamble/clojure-ants-simulation/blob/master/src/ants.clj
I have a sequence of maps representing tabular data rows, and i want to assoc in each row the running sum of the value of another "column":
(def rows
[{:price 10 :qty 6}
{:price 11 :qty 7}
{:price 15 :qty 20}
{:price 14 :qty 7}
{:price 13 :qty 8}
{:price 11 :qty 2}])
(defn with-reductions-col [old-key new-key rf rows]
(let [agg (drop 1 (reductions rf (rf) (map old-key rows)))]
(map (fn [row v] (assoc row new-key v)) rows agg)))
(with-reductions-col :qty :total + rows)
;;=>
({:price 10, :qty 6, :total 6}
{:price 11, :qty 7, :total 13}
{:price 15, :qty 20, :total 33}
{:price 14, :qty 7, :total 40}
{:price 13, :qty 8, :total 48}
{:price 11, :qty 2, :total 50})
Are there any libraries that help do this kind of thing efficiently, so I could append multiple rows in a single pass?Actually I just realized my old favorite https://github.com/cgrand/xforms probably does what i need with transjuxt + reductions
Perhaps incanter
can do it. and according to https://slabruyere.net/assets/pdf/2019-04-24-clojure-for-data-science.pdf other statistics lib may be able to do it.
i'm really just curious to learn more... is there documentation that would explain why i consistently get sets seq'd in the same order? for example:
user=> (seq #{1 2 3})
(1 3 2)
user=> (seq #{"1" "2" "3"})
("3" "1" "2")
i'm surprised to get that same (non-sequential) order on 2 different machines, even clj and cljs (which i was even more surprised to find)... i'm bewildered that the order appears reliable (even though i would never want to rely on it).
if anyone happens to have insight i'd love to know.I haven't checked it but I imagine it's due to how the hash function is implemented for those particular types (`long` and str
).
user=> #{1 2 3}
#{1 3 2}
user=> #{1 2 3 4}
#{1 4 3 2}
user=> #{1 2 3 4 5}
#{1 4 3 2 5}
user=> #{1 2 3 4 5 6}
#{1 4 6 3 2 5}
user=> #{1 2 3 4 5 6 7}
#{7 1 4 6 3 2 5}
user=> #{1 2 3 4 5 6}
#{1 4 6 3 2 5}
user=> #{1 2 3 4 5}
#{1 4 3 2 5}
user=> #{1 2 3 4}
#{1 4 3 2}
user=> #{1 2 3}
#{1 3 2}
@U01D8NEJ0EP try making sets of this
it is based on hash function (which is not guaranteed between versions/jvms/etc), but is stable if those variables are the same
i think java's hashset started purposefully shuffling the items in order to break people who relied on the order
I think there may be some cases where insertion/removal order around the internal tree order may result in different orders, but you'd need a bigger set and a series of "modifications"
I think there may be some cases where insertion/removal order around the internal tree order may result in different orders, but you'd need a bigger set and a series of "modifications"