I want to maintain a collection of channels, with the ability to add and remove channels. Is the equality defined so I can conj and disj correctly?
In other words, will this always work?
=> (def chan-collection (atom #{}))
=> (def my-chan-1 (chan))
=> (def my-chan-2 (chan))
=> #chan-collection
#{}
=> (swap! chan-collection conj my-chan-1)
=> #chan-collection
#{#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel#6ec3a2f6>}
=> (swap! chan-collection conj my-chan-2)
=> #chan-collection
#{#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel#382830a1>
#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel#6ec3a2f6>}
=> (swap! chan-collection disj my-chan-1)
=> #chan-collection
#{#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel#382830a1>}
=> (swap! chan-collection disj my-chan-2)
=> #chan-collection
#{}
Yes this is true, and changing it would break everything.
Channels are identical? if they are the same chan object, and therefore are equal in all cases. All other comparisons of chans are explicitly not equal, and this is good for you. You want removal to remove the exact chan you ask to be removed rather than some equivalent chan with the same contents. So it's fortunate that chans that are not identical are also not equal
user> (= (chan) (chan))
false
user> (identical? (chan) (chan))
false
user> (identical? my-chan-1 (chan))
false
user> (identical? my-chan-1 my-chan-1)
true
user>
user> (= my-chan-1 my-chan-1)
true
In the general "Clojure world" this same property is true of all things that are Identities rather than values. Identities have values that change over time so it doesn't make sense to say that two identities are equal if they happen to contain the same value at the moment you ask, even though this may only be true for you and never for anyone else. comparing the values in identities makes much more sense. for example like chans, atoms with the same values are also not equal and this is a fundamental property of clojure that will never change.
user> (let [a (atom 1)]
(= a a))
true
user> (= (atom 1) (atom 1))
false
Provided you want to remove them by giving the exact chan you want removed as an argument to disj, as you are doing above, rather than some other notion like "remove the channel with 42 in it"
If we do the same setup:
user> (require '[clojure.core.async :refer [<! <!! >! chan]])
nil
user> (def chan-collection (atom #{}))
#'user/chan-collection
user> (def my-chan-1 (chan))
#'user/my-chan-1
user> (def my-chan-2 (chan))
#'user/my-chan-2
user> (swap! chan-collection conj my-chan-1 my-chan-2)
#{#object[clojure.core.async.impl.channels.ManyToManyChannel 0x35b61c71 "clojure.core.async.impl.channels.ManyToManyChannel#35b61c71"] #object[clojure.core.async.impl.channels.ManyToManyChannel 0x240e86d5 "clojure.core.async.impl.channels.ManyToManyChannel#240e86d5"]}
and then ask to have "the empty chan" removed:
user> (swap! chan-collection disj (chan))
#{#object[clojure.core.async.impl.channels.ManyToManyChannel 0x35b61c71 "clojure.core.async.impl.channels.ManyToManyChannel#35b61c71"] #object[clojure.core.async.impl.channels.ManyToManyChannel 0x240e86d5 "clojure.core.async.impl.channels.ManyToManyChannel#240e86d5"]}
we can verify that it does nothing.
Related
clojure 1.9.0
A simple test of sort-by on the character array works below,
user=> (sort-by identity [[\B] [\a]])
([\B] [\a])
but why did another test fail to sort-by case-insensitively?
user=> (sort-by (partial map #(Character/toLowerCase %)) [[\B] [\a]])
java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to java.lang.Comparable
Solution
Using mapv instead of map makes it.
user=> (instance? clojure.lang.LazySeq (map identity []))
true
user=> (instance? clojure.lang.PersistentVector (mapv identity (map identity [])))
true
You don't need map:
(ns tst.demo.core
(:require
[clojure.string :as str] ))
(sort-by #(.toLowerCase (str/join %)) [[\a \b] [\B] [\a]])
;=> ([\a] [\a \b] [\B])
but why did another test fail to sort-by case-insensitively?
map returns a lazy sequence, which doesn't implement Comparable. mapv works because vectors support Comparable and that's what sort-by is using to sort.
(supers (type []))
=> #{... java.lang.Comparable ...}
(supers clojure.lang.LazySeq)
=> #{clojure.lang.IObj clojure.lang.ISeq clojure.lang.Seqable clojure.lang.IMeta java.lang.Iterable java.util.List clojure.lang.IHashEq java.lang.Object clojure.lang.Obj java.util.Collection clojure.lang.IPending clojure.lang.Sequential clojure.lang.IPersistentCollection java.io.Serializable}
What is the difference between def and defonce in Clojure?
When to use def over defonce or vice versa?
defonce is skipped when variable is already defined.
user> (def a 1) ;;=> #'user/a
user> a ;;=> 1
user> (def a 2) ;;=> #'user/a
user> a ;;=> 2
user> (defonce b 1) ;;=> #'user/b
user> b ;;=> 1
user> (defonce b 2) ;;=> nil
user> b ;;=> 1
Defonce only binds the name to the root value if the name has no root value.
For example, like Jay Fields blogs about, it can be used in conjunction when you want to reload namespaces but you might not need to reload all.
(defonce ignored-namespaces (atom #{}))
(defn reload-all []
(doseq [n (remove (comp #ignored-namespaces ns-name) (all-ns))]
(require (ns-name n) :reload )))
As for when to use defonce, if you're using system with hot reloading (CLJS with mount and re-frame for example), defonce is useful to keep the state between reloads.
Similar situation when you re-evaluate source file yourself (e.g. in REPL) but want to keep the value of the var bound to the symbol.
(ns example.asyncq
(:require
[cljs.core.async :as async]
goog.async.AnimationDelay)
(:require-macros
[cljs.core.async.macros :refer [go go-loop]]))
(defn loop-fx-on-atm-when-pred?-is-true [atm fx pred?]
(let [c (async/chan)
f (goog.async.AnimationDelay. #(async/put! c :loop))
a (atom false)]
(add-watch
atm
:loop-fx-on-atm-when-pred?-is-true
(fn [_ _ _ _]
(when-not #a
(async/put! c :initial-loop))))
(go-loop []
(async/<! c)
(reset! a true)
(swap! atm #(if (pred? %) (do (.start f) (fx %)) %))
(reset! a false)
(recur))))
(def the-final-countdown (atom 4))
(loop-fx-on-atm-when-pred?-is-true
the-final-countdown
#(do (js/console.log %) (dec %))
pos?)
(swap! the-final-countdown inc)
;; prints "5" "4" "3" "2" "1", each on a seperate frame (hearbeat)
The purpose of loop-fx-on.... is to register fx to be called on atm (per heartbeat) whenever pred? #atm is true.
I would like the following properties:
This code should only execute when pred? might return true. Otherwise, the state machine sits idle.
This should not register more than once per frame. Modifications of the atom after the AnimationDelay has been called but before the next actual frame are effectively noops. That is, the atom is modified, but it does not cause f to be called again.
From the point of view of the person using the atom, all they need do is modify the atom. They do not need to concern themselves with registering or unregistering the atom for AnimationFrame processing, it just works. The atom will continue to be processed every frame until pred? returns false.
My biggest gripe with the above code is (reset! a true) and (reset! a false) around the swap!. That is really ugly, and in a non single threaded environment would probably be a bug. Is there any way to improve this code so that I don't end up using a ?
My second concern is that 2 is not being met. Currently, if you modify the same atom twice between a frame, it will actually result in 2 calls to (f); or 2 callbacks on the next frame. I could use another atom to record whether I have actually registered for this frame or not, but this is already getting messy. Anything better?
Something like this might work for you:
(defn my-fn [a f pred]
(let [pending? (atom false)
f (fn []
(reset! pending? false)
(f #a))]
(add-watch a (gensym)
(fn [_k _a _old-val new-val]
(when (and (not #pending?) (pred new-val))
(reset! pending? true)
(if (exists? js/requestAnimationFrame)
(js/requestAnimationFrame f)
(js/setTimeout f 16)))))))
I'm trying to handle following DSL:
(simple-query
(is :category "car/audi/80")
(is :price 15000))
that went quite smooth, so I added one more thing - options passed to the query:
(simple-query {:page 1 :limit 100}
(is :category "car/audi/80")
(is :price 15000))
and now I have a problem how to handle this case in most civilized way. as you can see simple-query may get hash-map as a first element (followed by long list of criteria) or may have no hash-mapped options at all. moreover, I would like to have defaults as a default set of options in case when some (or all) of them are not provided explicite in query.
this is what I figured out:
(def ^{:dynamic true} *defaults* {:page 1
:limit 50})
(defn simple-query [& body]
(let [opts (first body)
[params criteria] (if (map? opts)
[(merge *defaults* opts) (rest body)]
[*defaults* body])]
(execute-query params criteria)))
I feel it's kind of messy. any idea how to simplify this construction?
To solve this problem in my own code, I have a handy function I'd like you to meet... take-when.
user> (defn take-when [pred [x & more :as fail]]
(if (pred x) [x more] [nil fail]))
#'user/take-when
user> (take-when map? [{:foo :bar} 1 2 3])
[{:foo :bar} (1 2 3)]
user> (take-when map? [1 2 3])
[nil [1 2 3]]
So we can use this to implement a parser for your optional map first argument...
user> (defn maybe-first-map [& args]
(let [defaults {:foo :bar}
[maybe-map args] (take-when map? args)
options (merge defaults maybe-map)]
... ;; do work
))
So as far as I'm concerned, your proposed solution is more or less spot on, I would just clean it up by factoring out parser for grabbing the options map (here into my take-when helper) and by factoring out the merging of defaults into its own binding statement.
As a general matter, using a dynamic var for storing configurations is an antipattern due to potential missbehavior when evaluated lazily.
What about something like this?
(defn simple-query
[& body]
(if (map? (first body))
(execute-query (merge *defaults* (first body)) (rest body))
(execute-query *defaults* body)))
The function below does 2 things -
Checks if the atom is nil or fetch-agin is true, and then fetches the data.
It processes the data by calling (add-date-strings).
What is a better pattern to separate out the above two concerns ?
(def retrieved-data (atom nil))
(defn fetch-it!
[fetch-again?]
(if (or fetch-again?
(nil? #retrieved-data))
(->> (exec-services)
(map #(add-date-strings (:time %)))
(reset! retrieved-data))
#retrieved-data))
One possible refactoring would be:
(def retrieved-data (atom nil))
(defn fetch []
(->> (exec-services)
(map #(add-date-strings (:time %)))))
(defn fetch-it!
([]
(fetch-it! false))
([force]
(if (or force (nil? #retrieved-data))
(reset! retrieved-data (fetch))
#retrieved-data)))
By the way, the pattern to seperate out concerns is called "functions" :)
To really separate the concerns I think it might be better to define a separate fetch and process function. So that in no way they are complected.
(def retrieved-data (atom nil))
(defn fetcher []
(->> (exec-services)
(map #(add-date-strings (:time %)))))
(defn fetch-again? [force]
(fn [data] (or force (nil? data))))
(defn fetch-it! [fetch-fn data fetch-again?]
(when (fetch-again? #data))
(reset! data (fetch-fn))))
;;Usage
(fetch-it! fetcher retrieved-data (fetch-again? true))
Notice that I also gave the data atom as an argument.