In Clojure, what does (-> ... ... ...) means? Changing the order of things results a different output.
(defn my-method
"description"
[& args]
(-> things/thing1
things/thing2
things/thing3
things/thing4
things/thing5))
This is "thread-first" macro:
(-> a b c)
is equivalent to
(c (b a))
Basically this macro is used to avoid nesting big amount of forms. It just inserts first form as first argument of second form: (-> a b) becomes (b a). Then resulting form is inserted as first argument of third form: (-> (b a) c) becomes (c (b a)). So, your example is equivalent to:
(defn my-method
"description"
[& args]
(things/thing5 (things/thing4 (things/thing3 (things/thing2 things/thing1)))))
(doc) is a convenient utility that is running in a Clojure REPL. It should be at the corner of everyday's development.
user=> (doc ->)
clojure.core/->
([x & forms])
Macro
Threads the expr through the forms. Inserts x as the
second item in the first form, making a list of it if it is not a
list already. If there are more forms, inserts the first form as the
second item in second form, etc.
Related
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.
My question is: how can I get the args list and expressions of a received function ?
I'm trying to do something like this:
(defn first-fn [[args exprs]]
(println "Args:" args)
(println "Exprs:" exprs))
(first-fn (fn [a b c] (println "something")))
So, first-fn would print:
Args: [a b c]
Exprs: (println "something")
My goal is to create a macro that can use the args list of the received function.
Thank you.
Edit:
Use case:
I'm using compojure https://github.com/weavejester/compojure
You can define routes like this:
(GET "/:id" [id] (body_here id))
But I would like to change the syntax to be:
(defn handler-fn [id] (body_here id))
...
(GET "/:id" handler-fn)
So the handler (body) can be extracted from the routes, and might be reused as well.
I tried to reuse compile-route https://github.com/weavejester/compojure/blob/master/src/compojure/core.clj#L172
(defmacro MY_GET [path fn-src]
(let [fn-fn (second fn-src)
arg-vec (nth fn-src 2)
forms (drop 3 fn-src)]
(compojure.core/compile-route :get path arg-vec forms)))
But when I call:
(MY_GET "/:id" handler-fn)
It says: Don't know how to create ISeq from: clojure.lang.Symbol
You cannot do this with functions, you directly need a macro to do this and even then it is not straight-forward. First, let's explain the difference: macros are basically evaluated at compile-time and the result of this evaluation is then evaluated at run-time. The interesting part is that the evaluation at compile-time gets the literal, unevaluated arguments to the macro as data and not, like normal functions would, the evaluated arguments at run-time. So, your approach cannot work, because at the time first-fn receives it's arguments (at run-time), they are already evaluated -- in your example, first-fn receives nil as arguments. Cf. the documentation at clojure-doc for a much better explanation.
Now, solving your request with a macro requires the macro to parse the arguments (remember: at compile time, code is data) that it receives -- i.e. in your example, it needs to parse the sequence (fn [a b c] (println "something")) that builds up the function call you hand over to it. Probably you would want to cover other cases besides the fn one (e.g. the # short-hand), that's what it makes the problem not straight-forward in the general case.
This parsing could in the end be handled by a normal function parsing, e.g. a sequence. So, try solving a different puzzle first: build a function parse-code-sequence that takes a sequence (that looks like the functions you would hand over) and returns the args and expr -- note the quote (') in front of fn.
user> (parse-code-sequence '(fn [a b c] (println "something")))
{args: [a b c], expr: (println "something")}
Some hints to this: in the example here, which is showing the most used case, the sequence just consists of three elements and you don't need the first one. But the general case is a little bit more complex, cf. the official documentation on fn.
A final remark: when you implement the macro, you need to think about what it resolves to -- just adding the print-statements is easy, but do you also want to evaluate the arguments normally (so your macro becomes something like a debugging aid) or do you want to do something else?
Update to reflect your use-case
Your MY-GET macro is not doing what you think it's doing.
Take a look at the arguments that the macro gets: why do you think it can magically retrieve the function definition of handler-fn, when all that you give as argument to MY_GET is the symbol/var handler-fn? You would need to retrieve the source, but this usually will not be possible (cf. this SO question on retrieving the source of a function definition).
You are also missing a backquote before the call to compile-route: you want the call to compile-route to happen at run-time, not at compile time. Currently, the result of the macro evaluation is the result of the call to compile-route (at compile-time). Take a look at macroexpand which would show you the result of the macro-expansion. Basically, you want the macro to return the call to compile-route.
I don't see any easy way that you could accomplish what you look for. The argument vector of a route definition is defining what needs to be handed over. Even if you extract that to a function definition, compojure still needs to know what to hand over to that function.
Here is an example of what you could do.
(ns xyz
(:require
[tupelo.core :as t]
))
(t/refer-tupelo)
(spyx *clojure-version*)
(defmacro dissect [ fn-src ]
(let [fn-fn (first fn-src)
arg-vec (second fn-src)
forms (drop 2 fn-src) ]
(spyx fn-fn)
(spyx arg-vec)
(spyx forms)
; Here is the return value; ie the transformed code
`(defn my-fn
~arg-vec
(apply + ~arg-vec))))
; show the result
(newline)
(println
(macroexpand-1
'(dissect
(fn [a b c]
(println "the answer is")
42))))
; call it for real
(newline)
(dissect
(fn [a b c]
(println "the answer is")
42))
; use the generated function
(newline)
(spyx (my-fn 1 2 3))
with result:
*clojure-version* => {:major 1, :minor 8, :incremental 0, :qualifier nil}
fn-fn => fn
arg-vec => [a b c]
forms => ((println "the answer is") 42)
(clojure.core/defn tst.clj.core/my-fn [a b c] (clojure.core/apply clojure.core/+ [a b c]))
fn-fn => fn
arg-vec => [a b c]
forms => ((println "the answer is") 42)
(my-fn 1 2 3) => 6
Your project.clj needs the following to make spyx work:
:dependencies [
[tupelo "0.9.11"]
In clojure, is it possible to execute multiple expressions in a cond case, without do or let form. In Racket, one may do things as:
(cond
(> a 0) ((display a) (display (* a a))
(= a 0) ...
...
Another form that contains an implicit do is when, so “yes,” you could achieve this without do or let:
(cond
(> a 0) (when true
(println a)
(println (* a a))))
But, of course, that's contrived and the real answer to your question is “no.”
Each clause has a pair of forms: A test form and an expression form. You can't have multiple expression forms in a cond clause—you'd need to further wrap them in a single form such as do. They are referred to as expression forms because usually their value is of interest (in a purely functional context) because it is the value of the expression in the clause with the first truthy test that the entire cond form evaluates to.
But, if you are using cond as a way to achieve conditional side effects, perhaps do is a good reinforcement of the intent:
(cond
(> a 0) (do (println a)
(println (* a a))))
Is there anyway to define a new macro under the name def in Clojure? I defmacroed a new one after trying to :refer-clojure :exclude the original, but it used the built-in definition anyway.
I was trying to enable Scheme-style function definitions ((def (f x y) ...) as (defn f [x y] ...)) using the following code.
(defmacro def
[ id & others ]
(if (list? id)
`(defn ~(first id) [~#(rest id)] ~#others)
`(def ~id ~#others)))
def is not a macro, it is a special form. It interns a symbol into current namespace. You cannot redefine special forms, but even if you could, how would you get same behavior?
The most straightforward way is to write define macro on base of def and defn. If you want to use def word, you can write a wrapper replace-my-def-with-normal-def around all the module, i.e.
(replace-my-def-with-normal-def
(def x 0)
(def y (inc x))
(def z (inc y))
(def (my-func a b) (println a b))
)
but I'm not sure it won't break other definitions, which depend on def (for example, defn).
You can create a macro with the name of a special form (like if), but it won't help you. Special forms are evaluated before macros, therefore if you have a macro and a special form of same name the special form will always be used. More information here http://clojure.org/evaluation.