If I call (keys m)
and (vals m)
for the same m
will the order of the keys match the order of the values?
It does seem to imply that
user=> (doc keys)
-------------------------
clojure.core/keys
([map])
Returns a sequence of the map's keys, in the same order as (seq map).
nil
user=> (doc vals)
-------------------------
clojure.core/vals
([map])
Returns a sequence of the map's values, in the same order as (seq map).
nil
ah, stupid me, should have just looked at the docstring
my eyes were fixated on the source code which led me into some decompiled Java bytecode…
That is promised for the identical map objects. It is not promised for two different map objects for which clojure.core/=
returns true.
good to know @andy.fingerhut, but thankfully it’s the same map instance
If in doubt, you could just do a seq
on the map which would deliver a list of [k, v]
pairs which you could then split.
In “https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/ClojureMadeSimple.md” Rich mentions the importance of “Loose coupling” Could someone explain what does he mean by “loose coupling” and how it applies in Clojure?
@viebel He talks more about it further into the preso: 40-50 mins in.
TL;DR: use generic, immutable data.
And nearly an hour in, he uses polymorphism a la carte as opposed to inheritance as another example for loose coupling.
There are other basic examples I have heard, e.g. use queues to connect two parts of software, rather than having one make calls to the other by name to pass on pieces of work.
The ideas of loose vs. tight coupling are, I believe, fairly language independent, although the mechanisms for doing so can of course differ between languages.
I suppose it would be accurate to say that loose coupling is more difficult to achieve in some programming languages than other.
@andy.fingerhut @seancorfield Would you agree with this example? In Clojure, a namespace that manipulates a map that represents customer data is loosely coupled with customer data. While in Java, a static method that manipulates a Customer record (or a immutable data class) is strongly coupled with Customer.
One benefit of Clojure approach is that the namespace doesn’t have to “import” the class definition for Customer
On the other hand, a Java developer might argue that the contract between code that manipulate Customer data and Customer data shape is not explicit
For instance, if you want to rename a field in Customer data, how would you discover all the pieced of code that need to be updated
?
> We should program the insides of our sytems like we progam the outsides
I think those are pretty reasonable claims. You could use Spec, if you really wanted to specify the fields that your function(s) required to be present.
When Rich gave his talk, there was no Spec
I think we should find answers to those objections without involving Spec
I suspect this discussion belongs in a different channel since it is more philosophical -- if you're comparing other languages -- than technical.
What channel? #data-oriented-programming maybe?
Sounds like a better place.
Then folks can opt-in if they wish.
There is also #other-languages
Moved the discussion to #other-languages
Clauses are [accessor predicate & args]
I need a structure that can store these clauses such that I can take an input value and get back a set of matching clauses, as in, If (apply (resolve predicate) (accessor input) args)
is true, return the whole "clause"
(def clause-db
'[[:a > 0]
[:b < 5]
[:c #{:a :b :c}]
[:d = 22]])
(get-matching-clauses
clause-db
{:a 1
:b 2
:c :key})
;;=>
;;; returns matching clauses
#{[:a > 0]
[:b < 5]
[:c #{:a :b :c}]}
I want the get-matching-clauses function to be as efficient as possible, so for instance the same accessor shouldn't be called twice on the same input even if it is used in multiple clauses.
I have a full working example/use case here: https://gist.github.com/jjttjj/ad9c6355d8b6741b43f23e90c7c77442
But I'm wondering if I'm reinventing some wheel. This is something like a rules engine however I don't need any working memory, inputs are only considered in isolation. And while the total number of relevant clauses will grow over time (by wrapping the clause db in an atom) only the set of clauses that exists when the input is compared is relevant. I just need function that can take a literal representation of some "matches", in a data structure that can be appended to, and an input, and check for the matches.
Any thoughts on solutions to this?Sounds like a rule engine in reverse - the rules are the messages while the facts are the clauses
Hmm I'm starting to feel maybe https://github.com/noprompt/meander will be useful here but I haven't fully wrapped my head around it yet
You can remove some dynamism from your solution, but otherwise it makes sense
(defprotocol IClause
(-applies? [clause msg]))
(defrecord Clause0 [accessor f]
IClause
(-applies? [_ msg]
(f (accessor msg))))
(defrecord Clause1 [accessor f a]
IClause
(-applies? [_ msg]
(f (accessor msg) a)))
(def clause-db
'[[:a > 0]
[:b < 5]
[:c #{:a :b :c}]
[:d = 22]])
(defn compile-clause
[[accessor f & args]]
(let [accessor (if (symbol? accessor) (resolve accessor) accessor)]
(case (count args)
0 (->Clause0 accessor f)
1 (apply ->Clause1 accessor f args))))
(def compiled-db
(mapv compile-clause clause-db))
(defn apply-clauses
[m]
(into
[]
(filter #(-applies? % m))
compiled-db))
(apply-clauses
{:a 1
:b 2
:c :key})
Thanks for this @ben.sless this looks good