I have two functions:
user=> (def tmp (classificator.db/get-questions classificator.db/db))
#'user/tmp
user=> (def result (map classificator.core/transform-data tmp))
#'user/result
First function return data which I pass to second function.
I would like to write in one expression without tmp variable:
user=> (-> (classificator.db/get-questions classificator.db/db) (map classificator.core/transform-data))
IllegalArgumentException Don't know how to create ISeq from: classificator.core$transform_data clojure.lang.RT.seqFrom (RT.java:542)
Looks like I don't mistake in the -> macros usage. How can I fix it?
The thread-first macro -> puts arguments in the first position of the remaining forms, but you want it to go at the end. You can use the thread-last macro ->> instead:
(->> (classificator.db/get-questions classificator.db/db)
(map classificator.core/transform-data))
or
(->> classificator.db/db
(classificator.db/get-questions)
(map classificator.core/transform-data)))
Related
Here's a working minimal example showing how Clojure can handle non-namespaced symbols:
(defmacro simple-macro [s]
(name `~s))
(str "And the answer is "
(simple-macro v1))
Now I'd like to do something more complicated. Inspired by this example:
(defn typical-closure []
(let [names (atom [])]
(fn [arg] (swap! names conj arg) #names)))
(def Q (typical-closure))
(Q 1)
(Q 2)
;; [1 2]
I now want to define a similar closure to take the names of undefined variables.
(defn take-names-fun []
(let [names (atom [])]
#((swap! names conj (simple-macro %)) (deref names))))
(def P (take-names-fun))
(P v1)
But this doesn't work as hoped; I get the error:
Unable to resolve symbol: v1 in this context
Is there a way to fix this so that we can add the name "v1" to the list of names defined above?
I tried using a macro instead (inspired by a syntax trick on page 21 of "Mastering Clojure Macros")... but this answer on ask.clojure.org says it doesn't make sense to define a closure over an atom in a macro.
(defmacro take-names-macro []
(let [names (atom [])]
`(fn [~'x] (swap! ~names conj (simple-macro ~'x)) (deref ~names))))
(def R (take-names-macro))
And indeed, I get another error here:
Can't embed object in code, maybe print-dup not defined:
However, there is no such restriction for using atoms inside defn. Maybe at the end of the day I need to put my symbols in a namespace...?
Not quite sure what it is that you're ultimately trying to accomplish.
But, since P is a function, it will always evaluate its arguments. So, if you pass it an undefined symbol, you'll get the error you got. Instead, you have to create a macro so that you can quote the undefined symbol (to stop the evaluation of the argument) and then pass that to P. Here is an example that does that.
user> (defn take-names-fun []
(let [names (atom [])]
(fn [arg] (swap! names conj (name arg)))))
#'user/take-names-fun
user> (def P (take-names-fun))
#'user/P
user> (defmacro PM [s] `(P (quote ~s)))
#'user/PM
user> (PM v1)
["v1"]
user> (PM v2)
["v1" "v2"]
user>
You might find the article on Evaluation in Clojure helpful.
#dorab's answer is nice.
But you could also tell yourself: "When entering undefined variables into a function, I have to quote them to avoid evaluation of them!"
So, after:
(defn typical-closure []
(let [names (atom [])]
(fn [arg] (swap! names conj arg) #names)))
(def Q (typical-closure))
Do:
user=> (Q 'v1)
[v1]
user=> (Q 'v2)
[v1 v2]
user=> (Q 3)
[v1 v2 3]
user=> (Q 'v4)
[v1 v2 3 v4]
user=>
In this way you don't need the macro and you can alternate between evaluated and not-evaluated arguments (undefined symbols).
So with the way fn's are written in clojure there is unfortunately no way to get the name of the var being passed as a param from within the fn body.. Someone with more experience with the clojure src may be able to explain better why that is, my initial guess would be that it has something to do with keeping thread local scopes isolated and lazy.
But there's absolutely nothing stopping you from writing a macro that wraps other macros using your closure idea!
Here's an example of how something like that may be written:
https://stackoverflow.com/a/11857444
I am working through Clojure for the Brave and True. In the chapter on macros there is this exercise:
Write a macro that defines an arbitrary number of attribute-retrieving functions using one macro call. Here’s how you would call it:
(defattrs c-int :intelligence
c-str :strength
c-dex :dexterity)
What these functions do is retrieve a value from a map. For example given: (def character {:name "Travis", :intelligence 20, :strength 23, :dexterity 13})
The result of (c-int character) would be 20 of course such a function could easily be defined as (def c-int #(:intelligence %))
This is the solution I came up with to the problem:
(defmacro defattrs
[& attributes]
`(let [attribute-pairs# (partition 2 (quote ~attributes))]
(map (fn [[function-name# attribute-key#]]
(def function-name# #(attribute-key# %)))
attribute-pairs#)))
The problem I am having is that def uses the generated symbol name instead of what it resolves to to define the function (which in hindsight makes sense given the usage of def). My attempts to use expressions with defining functions such as:
(let [x ['c-int :intelligence]]
(def (first x) #((second x) %)))
Have resulted in this error: CompilerException java.lang.RuntimeException: First argument to def must be a Symbol, compiling:(/tmp/form-init5664727540242288850.clj:2:1)
Any ideas on how I can achieve this?
There are ordinary manipulations that you do with the attributes parameter that don't need to be generated as forms:
splitting the attributes into attribute-pairs; and
defining the function to generate a def form for each pair.
Applying the above to your code, we get ...
(defmacro defattrs [& attributes]
(let [attribute-pairs (partition 2 attributes)]
(map (fn [[function-name attribute-key]]
`(def ~function-name #(~attribute-key %)))
attribute-pairs)))
The scope of the back-quote is restricted to the def we wish to generate.
The values of the function-name and attribute-key parameters of the function are inserted into the def form.
There is one problem remaining.
The result of the map is a sequence of def forms.
The first one will be interpreted as a function to
apply to the rest.
The solution is to cons a do onto the front of the sequence:
(defmacro defattrs [& attributes]
(let [attribute-pairs (partition 2 attributes)]
(cons 'do
(map (fn [[function-name attribute-key]]
`(def ~function-name ~attribute-key))
attribute-pairs))))
I've also abbreviated #(~attribute-key %) to the equivalent ~attribute-key within the back-quoted form.
Let's see what the expansion looks like:
(macroexpand-1 '(defattrs dooby :brrr))
;(do (def dooby :brrr))
Looks good. Let's try it!
(defattrs gosh :brrr)
(gosh {:brrr 777})
;777
It works.
You have found the use-case for the back-quote and tilde. Just try this:
(let [x ['c-int :intelligence]]
(eval `(def ~(first x) #(~(second x) %))))
(def character {:name "Travis", :intelligence 20, :strength 23, :dexterity 13})
(c-int character) => 20
The back-quote is similar to the single-quote in that it makes the next form into a data structure of lists, symbols, etc. The difference is that the data structure is intended to be used as a template, where internal bits can be substituted using the tilde. The cool part is that the tilde doesn't just substitute items, but works for live code that can be any arbitrary Clojure expression.
I am programming something that doesn't have side-effects, but my code is not very readable.
Consider the following piece of code:
(let [csv_data (if header_row (cons header_row data_rows) data_rows)]
)
I'm trying to use csv_data in a block of code. What is a clean way of conditioning on the presence of a header_row? I've looked at if-let, but couldn't see how that could help here.
I have run into similar situations with functional for-loops as well where I'm binding the result to a local variable, and the code looks like a pile of expressions.
Do I really have to create a separate helper function in so many cases?
What am I missing here?
Use the cond->> macro
(let [csv_data (cond->> data_rows
header_row (cons header-row)]
)
It works like the regular ->> macro, but before each threading form a test expression has to be placed that determines whether the threading form will be used.
There is also cond->. Read more about threading macros here: Official threading macros guide
First, don't use underscore, prefer dashes.
Second, there is nothing wrong with a little helper function; after all, this seems to be a requirement for handling your particular data format.
Third, if you can change your data so that you can skip those decisions and have a uniform representation for all corner cases, this is even better. A header row contains a different kind of data (column names?), so you might prefer to keep them separate:
(let [csv {:header header :rows rows}]
...)
Or maybe at some point you could have "headers" and "rows" be of the same type: sequences of rows. Then you can concat them directly.
The ensure-x idiom is a very common way to normalize your data:
(defn ensure-list [data]
(and data (list data)))
For example:
user=> (ensure-list "something")
("something")
user=> (ensure-list ())
(())
user=> (ensure-list nil)
nil
And thus:
(let [csv (concat (ensure-list header) rows)]
...)
i would propose an utility macro. Something like this:
(defmacro update-when [check val-to-update f & params]
`(if-let [x# ~check]
(~f x# ~val-to-update ~#params)
~val-to-update))
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> (:header :data1 :data2)
user> (let [header-row nil
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows cons)]
csv-data))
;;=> [:data1 :data2]
it is quite universal, and lets you fulfill more complex tasks then just simple consing. Like for example you want to reverse some coll if check is trueish, and concat another list...
user> (let [header-row :header
data-rows [:data1 :data2]]
(let [csv-data (update-when header-row data-rows
(fn [h d & params] (apply concat (reverse d) params))
[1 2 3] ['a 'b 'c])]
csv-data))
;;=> (:data2 :data1 1 2 3 a b c)
update
as noticed by #amalloy , this macro should be a function:
(defn update-when [check val-to-update f & params]
(if check
(apply f check val-to-update params)
val-to-update))
After thinking about the "cost" of a one-line helper function in the namespace I've came up with a local function instead:
(let [merge_header_fn (fn [header_row data_rows]
(if header_row
(cons header_row data_rows)
data_rows))
csv_data (merge_header_fn header_row data_rows) ]
...
<use csv_data>
...
)
Unless someone can suggest a more elegant way of handling this, I will keep this as an answer.
Is it possible to remove the let statement / avoid the intermediate 'x' in the following code?:
(let [x (f a)]
(when (pred? x) x))
I bumped into this problem in the following use case:
(let [coll (get-collection-somewhere)]
(when (every? some? coll) ; if the collection doesn't contain nil values
(remove true? coll))) ; remove all true values
So if the collection is free of nil values, only not-true values remain, like numbers, strings, or whatever.
So, I'm looking for something like this:
(defn pass-if-true [x pred?]
(when (pred? x) x))
Assuming that you don't want to define that pass-if-true function, the best you can do is an anonymous function:
(#(when (every? some? %)
(remove true? %))
(get-collection-somewhere))
You could also extract the predicate and transformation into parameters:
(#(when (%1 %3) (%2 %3))
(partial every? some?)
(partial remove true?)
(get-collection-somewhere))
The let form is necessary to prevent your collection-building function from running twice:
(f a) or (get-collection-somewhere)
This is a typical idiom and you are doing it correctly.
Of course, you don't need the let if you already have the collection and are not building inside this expression.
However, you may wish to see when-let:
https://clojuredocs.org/clojure.core/when-let
It can save some keystrokes in some circumstances, but this isn't one of them.
Is there a way to generically get metadata for arguments to a function in clojure? The answer posted in this question does not, actually, work in general:
user> (defn foo "informative dox!" [] 1)
#'user/foo
user> (defmacro get-docs [func] `(:doc (meta (var ~func))))
#'user/get-docs
user> (get-docs foo)
"informative dox!"
user> (get-docs (identity foo))
; Evaluation aborted.
user> (defn process-docs [f] (let [docs (get-docs f)] (reverse docs)))
; Evaluation aborted.
The second-to-last line doesn't work because you can't call var on the list (identity foo), and the last line doesn't even compile because the compiler complains about being unable to resolve f.
Most of the solutions for this problem I've found rely on the idea that you have access to the symbol in the function's definition, or something like that, so that you can do something like (resolve 'f) or (var f). But I want something that I can use on the argument to a function, where you don't know that information.
Essentially, I'd like an expression I can put in place of the question marks below to get the metadata of #'map:
(let [x map] (??? x))
its a mouthful though possible:
(let [x map]
(:doc (meta (second (first (filter #(and (var? (second %))
(= x (var-get (second %))))
(ns-map *ns*)))))))
produces the desired result:
"Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is\n exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments."
under the hood Namespaces are essentially maps of names to vars and the vars contain functions. you can search the contents of these vars for the one that matches the function you are seeking and then look at it's associated var and get the metadata from that var.