Multimethod dispatch on namespaced map namespace - clojure

Is it possible to dispatch based on a namespaced map namespace i.e. #:<this-thing>{}? Without hacks like printing or inspecting key prefixes?
I consider the last one hacky because a key prefix can be overridden:
(:qux/bar #:qux{:bar :baz}); => :baz
(:foo/bar #:qux{:foo/bar :baz}); => :baz
(:qux/bar #:qux{:foo/bar :baz}); => nil

A map might contain all qualified keyword keys from a certain namespace, or it may contain a mix of unqualified keys or qualified keys from multiple namespaces. Here's a function to get the set of all namespaces (as keywords) from qualified keyword keys in a map:
(defn key-namespaces
"Returns set of all namespaces of keys in m."
[m]
(->> (keys m)
(keep (comp keyword namespace))
(set)))
Now you can use that as a dispatch-fn on a multimethod:
(defmulti do-thing key-namespaces)
(defmethod do-thing #{:foo} [m] (prn m))
(do-thing #:foo{:bar 1})
;; #:foo{:bar 1}
(foo {:bar/bar 1})
;; no multimethod found exception
You could specify multiple namespace prefixes in that set, or you could use a different dispatch-fn based on your use case.

This is not possible as this is just the visual representation of the map produced by writer. You would have to do the checks by yourself if all the keys in your map share the same namespace. Also the last example won't be produced by the writer - it will emit namespaced map literal only when all the keys share the same namespace.

Related

Reuse structure definition code from clojure spec/keys

I have a spec definition that validates contents of incoming data. Since the data is a map of fields, I use spec/keys for validating it. For example:
(def person-data {:name "Jon Doe", :age 30})
(s/def ::name string?)
(s/def ::age pos-int?)
(s/def ::person-info (s/keys :req-un [::name ::age])
...
;validate data via spec and make sure no additional keys are included
(s/valid? ::person-spec some-input)
But an additional need I have is to make sure incoming data only contains the keys I want. (in this case :name and :age keys only. For that, I do something like:
(def permitted-keys [:age :name])
(select-keys some-input permitted-keys)
, ensuring only those keys get filtered in.
Is there a way I can reuse some code between my spec definition for the map structure (s/keys) and this additional step I take for filtering the allowed keys (permitted-keys)?
Perhaps by either extracting the list of keys from the s/keys definition, or by passing an existing vector of keys to s/keys?
I'd recommend looking at this macro as it covers all the bases and provides a generator, but here's another take that assumes your maps only used unnamespaced keys:
(defmacro keys-strict
[& args]
(let [{:keys [req opt req-un opt-un]} args
ks (into #{} (->> (concat req opt req-un opt-un)
(map #(keyword (name %)))))] ;; strip namespaces from keywords
`(s/and (s/keys ~#args) (s/map-of ~ks any?))))
The only trick here for reusing the same source of truth for the keys is that your key specs will be namespaced but your map keys will not. You could do the same without a macro, you just s/and your s/keys spec with a s/map-of spec or some other spec that restricts the keys allowed.
Is there a way I can reuse some code between my spec definition for the map structure (s/keys) and this additional step I take for filtering the allowed keys (permitted-keys)?
Yes, this is handled in the example above by splicing args onto the s/keys call, and done similarly in this more complete macro here.
Note: There may be situations where you really need to restrict what keys you'll accept, but I think it's generally advised to define map specs that are open for later extension.

assoc with argument clojure

If I have an hash-map and I want to assoc a value to it, and I get the key as an argument, what should i do?
(defn define [name type kind] "define new var in one of the tables"
(if (or (= type "static") (= type "field"))
(def classScope (assoc classScope name (list type kind (addCount kind))))
(def methodScope (assoc methodScope name (list type kind (addCount kind))))
)
)
My problem is that i can't use :name, and not 'name.
Thanks!!
Update: If you want your keys to be in keyword form, just call keyword on them....
(defn my-map-fn [name type kind]
(assoc some-map (keyword name) (some-fn type kind)))
e.g.
(my-map-fn "some-name" "some-type" "some-kind") => {:some-name some-val}
Note that you shouldn't use def inside of defn. It looks like you want to keep a map of data and as you call define you want to store some more data in that map. A way that I go about this is to use atoms (there are other ways too).
(defonce classScope (atom {})
(defonce methodScope (atom {}))
(defn define
"if you want a doc string it goes here"
[name type kind]
(swap! (if (#{"static" "field"} type) classScope methodScope)
#(assoc % name (list type kind (addCount kind)))))
the benefit here is you get atomic updates and calls to define that may happen really close together won't butt heads.
Let's start with your explanatory comment:
I'm trying to create an hash-map that's like a symbol table: I will
identify each variable by its name, and it will have a list with its
type, kind and index in the code.
A few thoughts:
Don't use a list for the variable's characteristics; use a map.
You can think of the name of a variable as
a plain old string
a symbol
a keyword
Any of these works as the key of a map entry. Keep it simple. Use a string.
You're going to need such a table for every scope. And a scope should know its enclosing scope.
The descriptors static and field are not types; nor are they
alternatives in - say - Java.
I suggest you look at clojure.spec and typed Clojure to see how similar problems are handled within Clojure.

appending values in a global map in clojure

I declared a map in clojure using
(def finalMap {})
I am appending values to it inside a function using assoc but they are not appending, the map is remaining empty. I think it is due to immutability, can I in some way make a global map mutable.The function is a recursive one and I am appending values each time the function is called.
(defn func [arg1 arg2]
;(map append inside let)
(dorun (for [i (range 0 index)]
(do
(func(arg1 arg2))))))
Can you help me with the correct way to do this?
If you want a mutable map then you should create an atom:
(def final-map (atom {}))
Also normally you would use assoc to add more key value pairs to it. However you will need to use swap! just to be able to call assoc:
(swap! final-map assoc :a "a value")
This will add a key/value pair where the key is the keyword :a and the value is the String "a value".
It might be good to view some other examples of using assoc. Realise that in the code above assoc is being called with the old value of final-map as its first argument, and returning the new value of final-map.

How to lookup all vars containing specific metadata

Suppose I've added specific metadata to my vars:
(defn ^:run-at-startup init []
(prn "Initializing...")
:done)
(meta (var init))
; {:arglists ([]), :ns #<Namespace user>, :name init, :end-column 34,
; :run-at-startup true, :column 1, :line 5, :file "NO_SOURCE_FILE", :end-line 5}
Then I would like to lookup all the vars (across different namespaces) that contains it. Is it possible?
Here is why. My app consists of several modules that must be initialized at startup. New modules could be added and existing removed (not at runtime, of course), and it's initializers must be called without knowing any specifics of the module. I think of adding metadata to initializers, then looking it all up and calling.
I would like to know if there are better ways.
So, if you require all the namespaces that contain your non-private initializers, all-ns is able to retrieve a list of those namespaces. If you do not know what namespaces exist, you can probably use e.g. tools.namespace to find out.
The following function finds all vars that contain a certain metadata key set to true, returning a seq of the vars' values.
(defn find-by-var-meta
[metadata-flag]
(->> (all-ns)
(mapcat ns-publics)
(keep
(fn [[_ v]]
(when (-> v meta metadata-flag)
(var-get v))))))
The resulting seq can then be traversed and everything that is a function can be called. So, in your case this should look like this:
(require '[my.namespace.initializers a b c])
(find-by-var-meta :run-at-startup) ;; => seq of initializers from the above ns.
And a quick check in the REPL:
(defn ^:run-at-startup add-one [x] (inc x)) ;; => #'user/add-one
((first (find-by-var-meta :run-at-startup)) 5) ;; => 6
(As seen here, you also don't need to specify a full map for metadata if you only want to set a key - or multiple ones - to true.)

what advantage is there to use 'get' instead to access a map

Following up from this question: Idiomatic clojure map lookup by keyword
Map access using clojure can be done in many ways.
(def m {:a 1}
(get m :a) ;; => 1
(:a m) ;; => 1
(m :a) ;; => 1
I know I use mainly the second form, and sometimes the third, rarely the first. what are the advantages (speed/composability) of using each?
get is useful when the map could be nil or not-a-map, and the key could be something non-callable (i.e. not a keyword)
(def m nil)
(def k "some-key")
(m k) => NullPointerException
(k m) => ClassCastException java.lang.String cannot be cast to clojure.lang.IFn
(get m k) => nil
(get m :foo :default) => :default
From the clojure web page we see that
Maps implement IFn, for invoke() of one argument (a key) with an
optional second argument (a default value), i.e. maps are functions of
their keys. nil keys and values are ok.
Sometimes it is rewarding to take a look under the hoods of Clojure. If you look up what invoke looks like in a map, you see this:
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L196
It apparently calls the valAt method of a map.
If you look at what the get function does when called with a map, this is a call to clojure.lang.RT.get, and this really boils down to the same call to valAt for a map (maps implement ILookUp because they are Associatives):
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L634.
The same is true for a map called with a key and a not-found-value. So, what is the advantage? Since both ways boil down to pretty much the same, performance wise I would say nothing. It's just syntactic convenience.
You can pass get to partial etc. to build up HOFs for messing with your data, though it doesn't come up often.
user=> (def data {"a" 1 :b 2})
#'user/data
user=> (map (partial get data) (keys data))
(1 2)
I use the third form a lot when the data has strings as keys
I don't think there is a speed difference, and even if that would be the case, that would be an implementation detail.
Personally I prefer the second option (:a m) because it sometimes makes code a bit easier on the eye. For example, I often have to iterate through a sequence of maps:
(def foo '({:a 1} {:a 2} {:a 3}))
If I want to filter all values of :a I can now use:
(map :a foo)
Instead of
(map #(get % :a) foo)
or
(map #(% :a) foo)
Of course this is a matter of personal taste.
To add to the list, get is also useful when using the threading macro -> and you need to access via a key that is not a keyword
(let [m {"a" :a}]
(-> m
(get "a")))
One advantage of using the keyword first approach is it is the most concise way of accessing the value with a forgiving behavior in the case the map is nil.