How do you do partial application in Clojure?
I have tried:
(dorun (map println ["1" "2" "3" "4"]))
which works.
(async/send! channel "hello")
works too. But if I try to apply partial application
(dorun (map (async/send! channel) ["1" "2" "3" "4"]))
or
(dorun (map #(async/send! channel) ["1" "2" "3" "4"]))
or
(apply (partial map (async/send! channel) ["1" "2" "3" "4"]))
It says
clojure.lang.ArityException: Wrong number of args (1) passed to: immutant.web.async/send!
What am I doing wrong?
In Clojure currying is different than in languages like ML, F# or Haskell.
There are 2 ways to do partial application in Clojure:
Making closure, where you can specify the exact order of arguments:
(fn [coll] (map str coll)) of #(map str %)
Using partial, which will substitute arguments in order they provided:
(partial map str)
When you call function with less arguments than it requires you'll get ArityException (unless it's a multi-arity function, that can accept different number of arguments).
Nevermind, this seems to work:
(dorun (map (partial async/send! channel) ["1" "2" "3" "4"]))
A bit confused why this didn't work
(dorun (map #(async/send! channel) ["1" "2" "3" "4"]))
Related
Seems like providing a default value in Associative destructuring is well documented. https://clojure.org/guides/destructuring
Any known way to supply a default value in sequential destructuring?
For instance:
(let [[hey you guys] ["do" "re"]]
(println hey)
(println you)
(println guys))
Output:
do
re
nil
How would you provide a default value for 'guys'?
Have tried
(let [[hey you (or guys "me")] ["do" "re"]]
(let [[hey you #(or % "me")] ["do" "re"]]
and a few variations of
(let [[hey you guys :or "me"] ["do" "re"]]
Thanks!
No I don't believe there is a way to offer default values in non-associative destructuring.
There would be more than one way to accomplish that, depending on what you're after. The closest to the snippets you provide might be:
(let [input ["do" "re"]
defaults ["def1" "def2" "def3" "def4"]
[hey you guys] (concat input (drop (count input) defaults))]
(println hey you guys)) ;; do re def3
If you only have a default value for the 3rd arg, then you can use:
(let [[hey you guys] (conj ["do" "re"] "def3")]
(println hey you guys)) ;; do re def3
or
(let [[hey you guys] ["do" "re"]
guys (or guys "def3")]
(println hey you guys)) ;; do re def3
You can find a good overview of Clojure destructuring here:
http://blog.brunobonacci.com/2014/11/16/clojure-complete-guide-to-destructuring/
You can get what you want with a simple function:
(defn apply-defaults
[vals defaults]
(vec (map-indexed
(fn [idx val-default]
(or (get vals idx) ; replaces both missing and `nil` values
val-default))
defaults)))
with result:
data => [:a nil :c]
defaults => [:def-a :def-b :def-c :def-d]
(apply-defaults data defaults) => [:a :def-b :c :def-d]
Note that you have to modify it if you want to retain any nil values in the input.
If the length is short, you can do this:
(let [[hey you guys] (merge defaults values)]
(println hey)
(println you)
(println guys))
Merge is a function that you must define/choose depending on the behaviour that you want, it will probably be one of these two (as in the previous answers):
Overwrite the values in defaults that are present in values
concat the values from the first one missing
I'm generating json as literally as I can in clojure. My problem is that certain branches of the json are only present if given parameters are given. Here is a sample of such a condition
(defn message-for
[name uuid & [generated-uuids]]
{:message {:id (generate-uuid)
:details {:name name}
:metadata {:batch (merge {:id uuid}
(when generated-uuids (let [batches (map #(array-map :id %) generated-uuids)]
{:generatedBatches batches})))}}})
Unfortunately the when/let part is quite ugly. This same could be achieved using when-let as following but it doesn't work because my map returns [] instead of a nil.
(defn message-for
[name uuid & [generated-uuids]]
{:message {:id (generate-uuid)
:details {:name name}
:metadata {:batch (merge {:id uuid}
(when-let [batches (map #(array-map :id %) generated-uuids)]
{:generatedBatches batches}))}}})
Any ideas if I could somehow make when-let consider an empty list/array/seq as false so I could clean up my code a bit?
not-empty returns its argument if it is not empty.
When using when-let with a collection, always use not-empty
to retain the collection type
make refactoring easier
expressivenes
(when-let [batches (not-empty (map ...))]
...)
In your case I'd however prefer something like this:
...
:metadata {:batch (cond-> {:id uuid}
(seq generated-uuids)
(assoc :generatedBatches (map ...)))}
...
Notice that all three of the advantages listed above where met, without a nested let.
Also notice a new advantage
easier to extend with more conditions lateron
seq returns nil on an empty input sequence so you could do:
(when-let [batches (seq (map #(array-map :id %) generated-uuids))]
{:generatedBatches batches}))}}})
I am getting this complaint when passing Integer constructor to map function :
=> (map Integer. ["1" "2" "3"])
CompilerException java.lang.ClassNotFoundException: Integer., compiling:(NO_SOURCE_PATH:1:1)
However when I wrap the constructor in a function everything works:
=> (defn str-to-int [str] (Integer. str))
=> (map str-to-int ["1" "2" "3"])
(1 2 3)
Why do I have to wrap Integer in another function to make this work? Is there a better way to make it work without creating additional function?
map takes in a function and interop uses a special forms like new . and ..
It is fairly easy to wrap these with anonymous function literals
for example
(map #(Integer. %) ["1" "2" "3"])
produces the desired result.
without java interop. if you just need to convert to digits.
; nrepl.el 0.2.0 (Clojure 1.5.1, nREPL 0.2.3)
user> (map read-string ["1" "2"])
(1 2)
user> (class (first *1))
java.lang.Long
Or if you really need Integer class
user> (map (comp int read-string) ["1" "2"])
(1 2)
user> (class (first *1))
java.lang.Integer
(first ["a" "b" "c"])
->
"c"
where I would expect:
(first ["a" "b" "c"])
->
"a"
I think I must have misunderstood something here ,any help appreciated!
Best Regards.
(defn binnd-to-name [name-string to-bind]
(bind-to-name name-string to-bind))
(defmacro bind-to-name [name-string stuff-to-bind]
`(def ~(symbol name-string) ~stuff-to-bind))
(defn bind-services [list-of-services]
(if (empty? list-of-services)
nil
(do
(binnd-to-name (first (first list-of-services)) (last (first list-of-services)))
(bind-services (rest list-of-services)))))
(bind-services [["*my-service*" se.foo.bar.service.ExampleService]])
ExampleService is a Java class on the classpath, which I want to bind to the symbol my-service.
The idea is to loop through a list of name-value pairs and bind each name to the value.
It is not working as expected though.
So somehow in this code something evaluated into "def first last" apparently.
Problem is with your macros not expanding as you expect
(defmacro bind-to-name [name-string stuff-to-bind]
`(def ~(symbol name-string) ~stuff-to-bind))
(defmacro bind-services [services]
`(do
~#(for [s services]
`(bind-to-name ~(first s) ~(second s)))))
(bind-services [["*my-service*" se.foo.bar.service.ExampleService]])
If you try this approach your def symbol sequence will properly expand.
No way!
user=> (doc first)
-------------------------
clojure.core/first
([coll])
Returns the first item in the collection. Calls seq on its
argument. If coll is nil, returns nil.
user=> (first ["a" "b" "c"])
"a"
I want to send var-args of a function to a macro, still as var-args.
Here is my code:
(defmacro test-macro
[& args]
`(println (str "count=" ~(count args) "; args=" ~#args)))
(defn test-fn-calling-macro
[& args]
(test-macro args))
The output of (test-macro "a" "b" "c") is what I want: count=3; args=abc
The output of (test-fn-calling-macro "a" "b" "c") is : count=1; args=("a" "b" "c") because args is sent as a single argument to the macro. How can I expand this args in my function in order to call the macro with the 3 arguments?
I guess I'm just missing a simple core function but I'm not able to find it. Thanks
EDIT 2 - My "real" code, shown in EDIT section below is not a valid situation to use this technique.
As pointed out by #Brian, the macro xml-to-cass can be replaced with a function like this:
(defn xml-to-cass
[zipper table key attr & path]
(doseq [v (apply zf/xml-> zipper path)] (cass/set-attr! table key attr v)))
EDIT - the following section goes beyond my original question but any insight is welcome
The code above is just the most simple I could come with to pinpoint my problem. My real code deals with clj-cassandra and zip-filter. It may also look over-engineering but it's just a toy project and I'm trying to learn the language at the same time.
I want to parse some XML found on mlb.com and insert values found into a cassandra database. Here is my code and the thinking behind it.
Step 1 - Function which works fine but contains code duplication
(ns stats.importer
(:require
[clojure.xml :as xml]
[clojure.zip :as zip]
[clojure.contrib.zip-filter.xml :as zf]
[cassandra.client :as cass]))
(def root-url "http://gd2.mlb.com/components/game/mlb/year_2010/month_05/day_01/")
(def games-table (cass/mk-cf-spec "localhost" 9160 "mlb-stats" "games"))
(defn import-game-xml-1
"Import the content of xml into cassandra"
[game-dir]
(let [url (str root-url game-dir "game.xml")
zipper (zip/xml-zip (xml/parse url))
game-id (.substring game-dir 4 (- (.length game-dir) 1))]
(doseq [v (zf/xml-> zipper (zf/attr :type))] (cass/set-attr! games-table game-id :type v))
(doseq [v (zf/xml-> zipper (zf/attr :local_game_time))] (cass/set-attr! games-table game-id :local_game_time v))
(doseq [v (zf/xml-> zipper :team [(zf/attr= :type "home")] (zf/attr :name_full))] (cass/set-attr! games-table game-id :home_team v))))
The parameter to import-game-xml-1 can be for example "gid_2010_05_01_colmlb_sfnmlb_1/". I remove the "gid_" and the trailing slash to make it the key of the ColumnFamily games in my database.
I found that the 3 doseq were a lot of duplication (and there should be more than 3 in the final version). So code templating using a macro seemed appropriate here (correct me if I'm wrong).
Step 2 - Introducing a macro for code templating (still works)
(defmacro xml-to-cass
[zipper table key attr & path]
`(doseq [v# (zf/xml-> ~zipper ~#path)] (cass/set-attr! ~table ~key ~attr v#)))
(defn import-game-xml-2
"Import the content of xml into cassandra"
[game-dir]
(let [url (str root-url game-dir "game.xml")
zipper (zip/xml-zip (xml/parse url))
game-id (.substring game-dir 4 (- (.length game-dir) 1))]
(xml-to-cass zipper games-table game-id :type (zf/attr :type))
(xml-to-cass zipper games-table game-id :local_game_time (zf/attr :local_game_time))
(xml-to-cass zipper games-table game-id :home_team :team [(zf/attr= :type "home")] (zf/attr :name_full))))
I believe that's an improvement but I still see some duplication in always reusing the same 3 parameters in my calls to xml-to-cass. That's were I introduced an intermediate function to take care of those.
Step 3 - Adding a function to call the macro (the problem is here)
(defn import-game-xml-3
"Import the content of xml into cassandra"
[game-dir]
(let [url (str root-url game-dir "game.xml")
zipper (zip/xml-zip (xml/parse url))
game-id (.substring game-dir 4 (- (.length game-dir) 1))
save-game-attr (fn[key path] (xml-to-cass zipper games-table game-id key path))]
(save-game-attr :type (zf/attr :type)) ; works well because path has only one element
(save-game-attr :local_game_time (zf/attr :local_game_time))
(save-game-attr :home :team [(zf/attr= :type "home"] (zf/attr :name_full))))) ; FIXME this final line doesn't work
Here's a some simple code which may be illuminating.
Macros are about code generation. If you want that to happen at runtime, for some reason, then you have to build and evaluate the code at runtime. This can be a powerful technique.
(defmacro test-macro
[& args]
`(println (str "count=" ~(count args) "; args=" ~#args)))
(defn test-fn-calling-macro
[& args]
(test-macro args))
(defn test-fn-expanding-macro-at-runtime
[& args]
(eval (cons `test-macro args)))
(defmacro test-macro-expanding-macro-at-compile-time
[& args]
(cons `test-macro args))
;; using the splicing notation
(defmacro test-macro-expanding-macro-at-compile-time-2
[& args]
`(test-macro ~#args))
(defn test-fn-expanding-macro-at-runtime-2
[& args]
(eval `(test-macro ~#args)))
(test-macro "a" "b" "c") ;; count=3; args=abc nil
(test-fn-calling-macro "a" "b" "c") ;; count=1; args=("a" "b" "c") nil
(test-fn-expanding-macro-at-runtime "a" "b" "c") ; count=3; args=abc nil
(test-macro-expanding-macro-at-compile-time "a" "b" "c") ; count=3; args=abc nil
(test-macro-expanding-macro-at-compile-time-2 "a" "b" "c") ; count=3; args=abc nil
(test-fn-expanding-macro-at-runtime "a" "b" "c") ; count=3; args=abc nil
If contemplation of the above doesn't prove enlightening, might I suggest a couple of my own blog articles?
In this one I go through macros from scratch, and how clojure's work in particular:
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html
And in this one I show why run-time code generation might be useful:
http://www.learningclojure.com/2010/09/clojure-faster-than-machine-code.html
The typical way to use a collection as individual arguments to a function is to use (apply function my-list-o-args)
(defn test-not-a-macro [& args]
(print args))
(defn calls-the-not-a-macro [& args]
(apply test-not-a-macro args))
though you wont be able to use apply because test-macro is a macro. to solve this problem you will need to wrap test macro in a function call so you can apply on it.
(defmacro test-macro [& args]
`(println ~#args))
(defn calls-test-macro [& args]
(eval (concat '(test-macro) (args)))) ;you almost never need eval.
(defn calls-calls-test-macro [& args]
(calls-test-macro args))
This is actually a really good example of one of the ways macros are hard to compose. (some would say they cant be composed cleanly, though i think thats an exageration)
Macros are not magic. They are a mechanism to convert code at compile-time to equivalent code; they are not used at run-time. The pain you are feeling is because you are trying to do something you should not be trying to do.
I don't know the library in question, but if cass/set-attr! is a function, I see no reason why the macro you defined has to be a macro; it could be a function instead. You can do what you want to do if you can rewrite your macro as a function instead.
Your requirements aren't clear. I don't see why a macro is necessary here for test-macro, unless you're trying to print the unevaluated forms supplied to your macro.
These functions provide your expected results, but that's because your sample data was self-evaluating.
(defn test-args
[& args]
(println (format "count=%d; args=%s"
(count args)
(apply str args))))
or
(defn test-args
[& args]
(print (format "count=%d; args=" (count args)))
(doseq [a args]
(pr a))
(newline))
You can imagine other variations to get to the same result.
Try calling that function with something that doesn't evaluate to itself, and note the result:
(test-args (+ 1 2) (+ 3 4))
Were you looking to see the arguments printed as "37" or "(+ 1 2)(+ 3 4)"?
If you were instead trying to learn about macros and their expansion in general, as opposed to solving this particular problem, please tune your question to probe further.