Here is my code:
(def partial-join (partial (clojure.string/join ",")))
=>(clojure.string/join "," ["foo" "bar"])
"foo,bar"
=> (partial-join ["foo" "bar"])
And it raises this exception:
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn .repl/eval12557 (form-init2162333644921704923.clj:1)
See the doc of clojure.string/join.
clojure.string/join
([coll] [separator coll])
Returns a string of all elements in coll, as returned by (seq coll),
separated by an optional separator.
when only one argument is provided for clojure.string/join, this function regard its argument as collection, so:
user=> (clojure.string/join ",")
","
Next, see the doc of partial.
clojure.core/partial
([f] [f arg1] [f arg1 arg2] [f arg1 arg2 arg3] [f arg1 arg2 arg3 & more])
Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args.
When only one argument provided, partial returns its argument.
user=> (partial (clojure.string/join ","))
","
Try this:
user=> (def partial-join (partial clojure.string/join ","))
#'user/partial-join
user=> (partial-join ["a" "b"])
"a,b"
The problem is NOT with the number of parameters passed to clojure.string/join. The problem is the brackets surrounding clojure.string/join call the function and the result of the function call is passed to partial. What you want to do is pass the function and the first param to partial as below:
(def partial-join (partial clojure.string/join ","))
(partial-join ["foo" "bar"])
;; => "foo,bar"
Related
In JavaScript, one can do the following:
function foo(arg1, arg2, arg3) {
...
}
var others = [ 'two', 'three' ];
foo('one', ...others); // same as foo('one', 'two', 'three')
In Clojure, "variable args" can be accepted like so:
(defn foo [arg1 & others]
...)
But to pass them in combination with other args, you have to do this:
(apply foo (concat '("one") others))
Which is frankly really ugly. It's also impossible when what you need to do is recur:
(apply recur (concat '("one") others)) ;; doesn't work
Is there a better way to do this? And if not, is there any way at all to accomplish it in the recur case?
But to pass them in combination with other args, you have to do this:
(apply foo (concat '("one") others))
You don't have to do that: apply is also a variadic function that can take arguments before the final sequence argument e.g.
(apply foo "one" others)
You can pass any number of individual arguments before the final sequence argument to apply.
user=> (defn foo [arg1 & args] (apply println arg1 args))
#'user/foo
user=> (apply foo "one" 2 "three" [4 "five" 6.0])
one 2 three 4 five 6.0
To further demonstrate, these calls to + are functionally equivalent:
(apply + 1 2 [3])
(apply + 1 [2 3])
(apply + [1 2 3])
It's also impossible when what you need to do is recur
recur is a special form, and apply doesn't work with it like it would a typical Clojure function.
is there any way at all to accomplish it in the recur case?
Not with apply. You can recur with variadic args, but you can't (apply recur ...).
I stumbled across implementation of partial function in cojure.core. It looks like this:
(defn partial
"Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args."
{:added "1.0"
:static true}
([f] f)
([f arg1]
(fn [& args] (apply f arg1 args)))
([f arg1 arg2]
(fn [& args] (apply f arg1 arg2 args)))
([f arg1 arg2 arg3]
(fn [& args] (apply f arg1 arg2 arg3 args)))
([f arg1 arg2 arg3 & more]
(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
Why it has several parity options if it could have one? Is it just performance optimisation so concat doesn't get called in most cases?
I mean it could look like this otherwise, right?
(defn partial
([f] f)
([f & more]
(fn [& args] (apply f (concat more args))))
)
I also noticed several other functions follow the same pattern.
Yes, it's a performance optimization.
I'ts not just about not calling concat - it's about the fact that & in the argument list requires a collection to be created as well. The clojure core libraries tend to take performance seriously, under the assumption that the basic building blocks of the language will be present in everyone's performance bottleneck.
I am trying to write a container function that takes a function name, it's arguments, and calls the function with an additional argument.
I am pretty sure I am doing this wrong, but here's what I've got so far.
(defn get-dyn [& args]
((first args) "some" (rest args)))
So, the first argument is the function name, and that function can have any number of arguments so I guess I have to accept optional parameter?
I need to inject a common argument "some" into all the functions I call via this function.
so,
(get-dyn str 3 4)
(get-dyn str 32 "willhelm")
etc.. and I would expect it to be equivalent of doing
(str "some" 3 4)
(str "some" 32 "wilhelm")
Except, the (rest args) returns a set so it doesn't work that way.
I tried doing it with partial but couldn't work it out.
Is there a way I can solve this problem? or am I better of using induvidual functions?
You can use apply.
user> (defn get-dyn [f & args]
(apply f "some" args))
#'user/get-dyn
user> (get-dyn str 1 2)
"some12"
user> (get-dyn str " " 1 " " 2)
"some 1 2"
Say I have a function that needs two arguments and where the order of arguments affects the results.
Is it possible to pass the first argument into a partial or comp function and the other aside of it, like this:
(defn bar [arg1 arg2] (- arg1 arg2))
(def baz (partial (bar arg1)))
(def qux (comp str (bar arg1)))
(baz arg2)
(qux arg2)
If I want to pass arg2 into the function, could I do something like this?
(def baz2 (partial (bar _ arg2)))
(def qux2 (comp str (bar _ arg2)))
(baz arg1)
(qux arg1)
partial only "fills in" arguments on the left side, so if you need to skip arguments you must use fn:
(def baz2 #(bar %1 arg2))
Note also that comp requires that all its arguments be functions, so your qux and qux2 are actually nonsense. They should be:
(def qux (comp str baz))
(def qux2 (comp str baz2))
In general, Clojure core functions will place the variable most likely to change last to make composition with comp and partial more natural. (E.g., the collection argument is almost always last in Clojure, except for things like into where it makes sense to put it first.) When you design your own functions you should stick to this convention so you can compose your functions more easily.
Scheme SRFI 26 has a useful macro cut for slotting parameters like this.
Usage would be like so for your subtracting bar:
((cut bar <> 1) 2)
;=> 1
((comp str (cut - <> 1)) 2)
;=> "1"
Where the <> symbol represents the slot to be filled.
It is a fun exercise to implement a cut in Clojure yourself, but here is one port by #Baishampayan Ghose.
Here's what I got in a repl:
user=> (defn bar [arg1 arg2] (- arg1 arg2))
#'user/bar
user=> (def baz (partial bar 5))
#'user/baz
user=> (def qux (comp str baz))
#'user/qux
user=> (baz 2)
3
user=> (qux 2)
"3"
It's the closest I could get from your first example.
For the second example, maybe a simple defn is better than a partial def:
user=> (defn baz2 [x] (bar x 5))
#'user/baz2
user=> (def qux2 (comp str baz2))
#'user/qux2
user=> (baz2 2)
-3
user=> (qux2 2)
"-3"
I'd suggest you start a repl and try it yourself, it's the second best way to discover a language that I know of (TDD being the first).
Cheers!
I am discussing closure with a friend and he thinks (partial + 5) is a closure. But I think a closure is a function closing over a free variable, for example
(let [a 10]
(defn func1 [x] (+ x a))
)
then func1 is a closure. But in this case 5 is not a free variable. So which is the right answer?
partial uses a closure to make the partial function. Check out the code of partial by using (source partial) in repl and you will see that it uses closures.
(defn partial
"Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args."
{:added "1.0"}
([f arg1]
(fn [& args] (apply f arg1 args)))
([f arg1 arg2]
(fn [& args] (apply f arg1 arg2 args)))
([f arg1 arg2 arg3]
(fn [& args] (apply f arg1 arg2 arg3 args)))
([f arg1 arg2 arg3 & more]
(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
(partial + 5) is an anonymous function or "lambda".
Anonymous functions are often¹ called "closures" but it's an abuse of the term ; see the discussion in "What is the difference between a 'closure' and a 'lambda'?"
[¹] Maybe because in most popular languages that support them, closures and anonymous functions are created using the same language features - which renders them undistinguishable at first glance.