Clojure: Function to determine map's value - clojure

The more I think about this problem, the more wrong it seems...
I have defined in my program something like a 'map constructor'. The idea behind this is that I have a generic map structure to handle some 'items' but I want to enforce some defaults for specific kind of items.
The problem that I have is that this 'map constructor' has a k-v pair, and that pair's value should be determined by the function that consumes this map (it might get clearer in the following example).
My first idea was to quote an expression in the value and then do an eval on it in the said function. The second idea was to replace the value with a fn, but this seems to return something similar to the quoted expression.
Let me try to depict the problem:
The model resulting map should be something like {:a 1 :b 2 :c 3}
The constructor is something like
(defn cons-field [b]
{:a (fn [name] (str name "!"))
:b b
:c "default"})
The item is created (def a-field (cons-field 5))
The calling function that consumes the map is something like
(defn the-function [name field]
(str (get-in field [:a])))
Now what I need is this :a's value to be a function of the parameter name in 'the-function'. Of course the last function is not working and I'm not sure if it's the correct approach anyway. The ':a' key is not always a fn; sometimes it's just a string literal. Any ideas?
Cheers!

So this is how I solved this problem after the comments of A. Webb and Jeremy Heiler.
The initial constructor was changed to this:
(defn cons-field [b]
{:a nil ; either delete completely or comment that
; the case will be handled by the caller
:flag xx ; true or :case-qualifier
:b b
:c "default"})
The calling func was changed to this:
(defn the-function [name field]
(let [case-q (:flag field)]
(cond
(= case-q :case-qualifier) (get-name name) ; you can have many different cases
; conciser using constants for these qualifiers
(...) ()))) ; else as normal
Then the logic initially put in the map goes in a different func:
(defn get-name [name] (str name "!"))
Hope this helps someone else :)

It is not really possible to understand your problem based on what you have posted. All I can do for you is tell you what your provided code does and guess what you would want it to do.
(def r (cons-field 5)) creates a hash-map r with (r :b) = 5, (r :c) = "default" and (r :a) = (fn [name] (str name "!")). As you can see, neither the result of (r :a) nor the result of (r :c) is related to r or 5.
If the results described above is what you want, it would be only logical to do some refactoring:
(def default-field {:a (fn [name] (str name "!"))
:c "default"})
(def cons-field (partial assoc default-field :b))
Regarding the-function: A call to (get-in field [:a]) is the same as (field :a) and will return the function (fn [name] ...). the-function will stringify this fn using str and most likely return something like "user.fn$23092...."
If you want the-function to return the result of calling (fn [name] ...) with name as passed to the function, you need to change your the-function as follows:
(defn the-function
[name field]
(str ((field :a) name)))
If you want the function to return something else based on the actual value of :a, you could check if it is a function and invoke it with name, otherwise return the value.
(defn the-function
[name field]
(when-let [v (field :a)]
(or (when (fn? v)
(v name))
v)))
Reading from your own answer now I am even more confused what actual problem you are trying to solve, but I hope that this answer could provide help.

Related

Clojure call series of functions and store their return values

I'm building a datomic schema and have the following at the foot of my clj file which defines and transacts schema and initial data. The functions being called below each call d/transact.
(defn recreate-database []
"To recreate db after running delete-database in bin/repl"
(pt1-transact-schema)
(pt1-transact-data)
(pt2-transact-schema)
(pt2-transact-data)
(pt3-transact-schema)
(pt3-transact-data))
By default we only see the return value of the last form, but I'd like to see, or save, the result of each of the six function calls.
Wondering what a nice way to do this is.
Thought of something like (map (comp println eval) [functions]), but that's not right.
there is also a nice functional composition function called juxt:
user> ((juxt + - * /) 1 2)
;;=> [3 -1 2 1/2]
user> ((juxt (constantly 1) (constantly 2) (constantly 3)))
;;=> [1 2 3]
or in your case:
(def recreate-database (juxt pt1-transact-schema
pt1-transact-data
pt2-transact-schema
pt2-transact-data
pt3-transact-schema
pt3-transact-data))
You could try this:
(defn recreate-database []
"To recreate db after running delete-database in bin/repl"
(mapv #(%) [pt1-transact-schema
pt1-transact-data
pt2-transact-schema
pt2-transact-data
pt3-transact-schema
pt3-transact-data]))
The expression #(%) is a shorthand notation for a lambda function that takes one argument, representing a function, and calls that function. If you find it more readable, you can replace that expression by (fn [f] (f)).
With datomic, all you need is a connection and a list of tx-data. Then you can use map to return the transact result on each step (i.e. each tx-data):
(defn recreate-database [conn & tx-data]
(->> tx-data
(map (partial d/transact conn))
doall))

clojure same ":or" value for all keys

I've defined a record with a bunch of fields--some of which are computed, some of which don't map directly to keys in the JSON data I'm ingesting. I'm writing a factory function for it, but I want to have sensible default/not-found values. Is there a better way that tacking on :or [field1 "" field2 "" field3 "" field4 ""...]? I could write a macro but I'd rather not if I don't have to.
There are three common idioms for implementing defaults in constructor functions.
:or destructoring
Example:
(defn make-creature [{:keys [type name], :or {type :human
name (str "unnamed-" (name type))}}]
;; ...
)
This is useful when you want to specify the defaults inline. As a bonus, it allows let style bindings in the :or map where the kvs are ordered according to the :keys vector.
Merging
Example:
(def default-creature-spec {:type :human})
(defn make-creature [spec]
(let [spec (merge default-creature-spec
spec)]
;; ....
))
This is useful when you want to define the defaults externally, generate them at runtime and/or reuse them elsewhere.
Simple or
Example:
(defn make-creature [{:keys [type name]}]
(let [type (or type :human)
name (or name (str "unnamed-" (name type)))]
;; ...
))
This is as useful as :or destructoring but only those defaults are evaluated that are actually needed, i. e. it should be used in cases where computing the default adds unwanted overhead. (I don't know why :or evaluates all defaults (as of Clojure 1.7), so this is a workaround).
If you really want the same default value for all the fields, and they really have to be different than nil, and you don't want to write them down again, then you can get the record fields by calling keys on an empty instance, and then construct a map with the default values merged with the actual values:
(defrecord MyFancyRecord [a b c d])
(def my-fancy-record-fields (keys (map->MyFancyRecord {})))
;=> (:a :b :c :d)
(def default-fancy-fields (zipmap my-fancy-record-fields (repeat "")))
(defn make-fancy-record [fields]
(map->MyFancyRecord (merge default-fancy-fields
fields)))
(make-fancy-record {})
;=> {:a "", :b "", :c "", :d ""}
(make-fancy-record {:a 1})
;=> {:a 1, :b "", :c "", :d ""}
To get the list of record fields you could also use the static method getBasis on your record class:
(def my-fancy-record-fields (map keyword (MyFancyRecord/getBasis)))
(getBasis is not part of the public records api so there are no guarantees it won't be removed in future clojure versions. Right now it's available in both clojure and clojurescript, it's usage is explained in "Clojure programming by Chas Emerick, Brian Carper, Christophe Grand" and it's also mentioned in this thread during a discussion about how to get the keys from a record. So, it's up to you to decide if it's a good idea to use it)

clojure: dynamically compose query with korma

I am trying to create a very simple API with korma
Users can query a database like so:
localhost:8080/my_postgres_db/users.json?where[age]=50&limit=1
Currently I am getting an error when trying to apply a where clause to an existing, composable, query.
clojure.lang.ArityException: Wrong number of args (2) passed to: core$where
The code in question:
(defn- comp-query [q [func arg]]
(let [sql-fn (ns-resolve 'korma.core (-> func name symbol))]
(sql-fn q arg)))
(defn compose-query [table col]
(reduce comp-query (select* table) col))
Usage:
(def clauses {:where {:column1 10} :fields "a,b" :limit 10 :offset 500})
(-> (compose-query table clauses) select)
Everything behaves as expected, except for where clauses. I can combine limit, offset and fields in any way I choose and I get the expected results. Only when I have a :where key in my map do I run into the error.
Am I attempting something I shouldn't? Is this bad clojure? Any help would be appreciated.
Note: I have read this SO question
Edit: from lein repl I can manually compose a query in the same fashion and it works
(where (select* "my_table") {:a 5})
Edit:
If I modify my compose-query function to this:
(defn compose-query [table col]
; remove where clause to process seperately
(let [base (reduce comp-query (select* table) (dissoc col :where))]
(if-let [where-clause (:where col)]
(-> base (where where-clause))
base)))
Everything works as expected.
The problem here is that korma.core/where is not a function and needs to be handled specially. Where can't be implemented as a function and still correctly handle things like (where query (or (= :hits 1) (> :hits 5)))
You can use where* function as you are using select*.
Just make your clause map like:
(def clauses {:where* {:column1 10} :fields "a,b" :limit 10 :offset 500})
Just a hunch; expanding some of the threading macros makes it a little hard to see if they are correct:
core> (macroexpand-1 '(-> (compose-query table clauses) select))
(select (compose-query table clauses))
core> (macroexpand-1 '(-> func name symbol))
(clojure.core/-> (clojure.core/-> func name) symbol)
core> (macroexpand-1 '(clojure.core/-> func name))
(name func)
Passing func to name looks suspicious.

Clojure: Pass 'expanded' optional args to function

I'm new to Clojure and I'm stuck on how to 'expand' a function's optional args so they can be sent to another function that uses optional args (but wants those args as keywords not a seq of keywords).
I'm parsing xml and if I hard code values as below my function works, it walks the xml and finds the value of 'title':
; zd was required like this
[clojure.data.zip.xml :as zd]
; ...
(defn get-node-value [parsed-xml & node-path]
(zd/xml-> (zip/xml-zip parsed-xml) :item :title zd/text))
(get-node-value parsed-xml)
What I want to do is use 'node-path' to pass in any number of keywords, but when written as below it comes in as a sequence of keywords so it throws an exception:
(defn get-node-value [parsed-xml & node-path]
(zd/xml-> (zip/xml-zip parsed-xml) node-path zd/text))
(get-node-value parsed-xml :item :title)
; ClassCastException clojure.lang.ArraySeq cannot be cast to clojure.lang.IFn clojure.data.zip/fixup-apply (zip.clj:73)
thanks!
I think you are looking for apply (http://clojuredocs.org/clojure_core/clojure.core/apply)
(defn get-node-value [parsed-xml & node-path]
(let [params (concat node-path [zd/text])]
(apply zd/xml-> (zip/xml-zip parsed-xml) params)))
Maybe you want:
(defn get-node-value [parsed-xml & node-path]
(zd/xml-> (zip/xml-zip parsed-xml) ((apply comp (reverse node-path))) zd/text))
I can't test the above, so am working from analogy:
(-> {:a {:c 1}, :b 2} ((apply comp (reverse [:a :c]))))
1
However, if dAni's solution works, ignore me!

Make a namespace qualified function name in a macro

I have a bunch of functions that map to and from some codes defined by an external system:
(defn translate-from-ib-size-tick-field-code [val]
(condp = val
0 :bid-size
3 :ask-size
5 :last-size
8 :volume))
(defn translate-to-ib-size-tick-field-code [val]
(condp = val
:bid-size 0
:ask-size 3
:last-size 5
:volume 8))
I'd like to make a macro to remove the duplication:
#_ (translation-table size-tick-field-code
{:bid-size 0
:ask-size 3
:last-size 5
:volume 8})
I started the macro like this:
(defmacro translation-table [name & vals]
`(defn ~(symbol (str "translate-to-ib-" name)) [val#]
(get ~#vals val#)))
The resulting function body seems right, but the function name is wrong:
re-actor.conversions> (macroexpand `(translation-table monkey {:a 1 :b 2}))
(def translate-to-ib-re-actor.conversions/monkey
(.withMeta (clojure.core/fn translate-to-ib-re-actor.conversions/monkey
([val__10589__auto__]
(clojure.core/get {:a 1, :b 2} val__10589__auto__))) (.meta ...
I'd like the "translate-to-ib-" to appear as part of the function name, instead of a prefix to the namespace, as it turned out.
How can I do this with clojure macros? If I am just doing it wrong and shouldn't use macros for this for some reason, please do let me know, but I would also like to know how to create function names like this to just improve my understanding of clojure and macros in general. Thanks!
The macro issue is twofold:
1) You're using a backtick when quoting the form passed to macroexpand, which namespace-qualifies the symbols within:
`(translation-table monkey {:a 1 :b 2})
=> (foo.bar/translation-table foo.bar/monkey {:a 1, :b 2})
where foo.bar is whatever namespace you're in.
2) You're constructing the name of the defn item using the symbol name, which, when it is namespace-qualified, will stringify to "foo.bar/monkey". Here's a version that will work:
(defmacro translation-table [tname & vals]
`(defn ~(symbol (str "translate-to-ib-" (name tname))) [val#]
(get ~#vals val#)))
Notice that we're getting the name of tname without the namespace part, using the name function.
As for whether a macro is the right solution here, probably not :-) For a simple case like this, I might just use maps:
(def translate-from-ib-size-tick-field-code
{0 :bid-size
3 :ask-size
5 :last-size
8 :volume})
;; swap keys & vals
(def translate-to-ib-size-tick-field-code
(zipmap (vals translate-from-ib-size-tick-field-code)
(keys translate-from-ib-size-tick-field-code)))
(translate-from-ib-size-tick-field-code 0)
=> :bid-size
(translate-to-ib-size-tick-field-code :bid-size)
=> 0
If speed is of the essence, check out case.
Some unsolicited advice on a different point: (get ~#vals val#) is extremely suspicious. Your macro alleges to take any number of arguments, but if it gets more than two it will just do something that doesn't make any sense. Eg,
(translation-table metric {:feet :meters}
{:miles :kilometers}
{:pounds :kilograms})
aside from being a terrible translation table, expands to code that always throws an exception:
(defn translate-to-ib-metric [val]
(get {:feet :meters}
{:miles :kilometers}
{:pounds :kilograms}
val)))
get doesn't take that many arguments, of course, and it's not really what you meant anyway. The simplest fix would be to only permit two arguments:
(defmacro translation-table [name vals]
(... (get ~vals val#)))
But note that this means the value map gets reconstructed every time the function is called - problematic if it's expensive to compute, or has side effects. So if I were writing this as a macro (though see Justin's answer - why would you?), I would do it as:
(defmacro translation-table [name vals]
`(let [vals# ~vals]
(defn ~(symbol (str "translate-to-ib-" name)) [val#]
(get vals# val#))))