This question already has answers here:
Generating a symbol from a string and applying it as a function
(2 answers)
How to convert a string to a function in Clojure?
(2 answers)
clojure resolving function from string name
(1 answer)
Call functions read from EDN files
(1 answer)
Closed 2 years ago.
The following works:
((resolve (symbol "first")) [1 2 3])
;; => 1
Why is it wrong to think that,
((read-string "first") [1 2 3])
;; => nil
should work, but it doesn't? (I get nil.)
The return value from (resolve (symbol "first")) is probably the Var clojure.core/first which gets applied to the arguments.
The return value from (read-string "first") is the symbol first which also gets applied to the arguments. But using a symbol as a function has a different meaning. The argument is expected to be a map and the returned value is the equivalent of doing (get a-map the-symbol).
Any type that implements the clojure.lang.IFn can be in the function position. The reason why using a symbol as a function with a vector argument returns nil instead of failing, lies in the implementation details of IFn for the Symbol type (in this particular case for the arity 1):
public Object invoke(Object obj) {
return RT.get(obj, this);
}
RT.get() checks if obj implements ILookup and calls ILookup.valAt() if it does. Clojure vectors do implement ILookup but they expect an integer as the provided key and return nil for anything else.
public Object valAt(Object key, Object notFound){
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
if(i >= 0 && i < count())
return nth(i);
}
return notFound;
}
public Object valAt(Object key){
return valAt(key, null);
}
Juan is correct:
(resolve (symbol "first")) => <#clojure.lang.Var #'clojure.core/first>
(read-string "first") => <#clojure.lang.Symbol first>
We can verify:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(dotest
(let [first-var (var clojure.core/first)
first-sym (symbol "first")]
(is= 1 (first-var [1 2 3])) ; automatically converted from a var => funciton by Clojure
(is= nil (first-sym [1 2 3]))))
Please also see this answer for more information on the double-indirection of a Clojure Var object.
Related
This question already has an answer here:
using clojure symbol function to make indirect function call
(1 answer)
Closed yesterday.
Suppose you have a peculiar map where each key might represent a Clojure core function:
{:inc 1 :dec 2 :identity "three"}
What would you write to apply each key (as a function) to its own value?
Output should yield something like:
(2 1 "three")
This code fails to produce what I expect:
user=> (def mm {:inc 1 :dec 2 :identity "three"})
user=> (map #((symbol (first %)) (get % 1)) mm)
(nil nil nil)
Wrapping the symbol with resolve solves this problem:
(resolve (symbol (...)))
More detailed explanation here: https://stackoverflow.com/a/63208807/4903731
Any of the following produce the desired output:
(def mm {:inc 1 :dec 2 :identity "three"})
(map (fn [%] ((resolve (symbol (key %))) (val %))) mm)
(map #((resolve (symbol (key %))) (val %)) mm)
(map #((resolve (symbol (first %))) (get % 1)) mm)
I have the following two test cases in Clojure, one destructuring a vector, and one destructuring a list:
user=> ((fn [[a op b]] (op a b)) [1 + 2])
3
user=> ((fn [[a op b]] (op a b)) '(1 + 2))
2
Why is the symbol + evaluated in the first test case but not the second? Is the quote recursive, so in the second test case op is really 'op?
You are correct that the single quote ' (equivalent of the function quote) will return a list of symbols, suppressing evaluation. However, since symbols cannot start with a number, the PersistentList '(1 + 2) will contain a java.lang.Long, a clojure.lang.Symbol, and a java.lang.Long.
When you invoke the function given by the Symbol + (which is possible because Symbol extends AFn which itself implements IFn, and overrides the invoke function), this gets executed:
public Object invoke(Object obj, Object notFound) {
return RT.get(obj, this, notFound);
}
Since you executed (op a b), or ('+ 1 2), obj is 1, notFound is 2, and this is the Symbol + in the call to clojure.lang.RT's get method, which looks like this:
static public Object get(Object coll, Object key, Object notFound){
if(coll instanceof ILookup)
return ((ILookup) coll).valAt(key, notFound);
return getFrom(coll, key, notFound);
}
Again, coll is the Long 1, key is the Symbol +, and notFound is the Long 2.
So when a function is called, it's being looked up in a collection. Since 1 is not a collection, the value notFound, or 2, is being returned.
As a visual of a ('+ 5 10) example, in a debugger:
First we can simply see if the forms are destructured the expected way or not with a simple println:
(def fn1
(fn [[a op b]]
(println "a>" a)
(println "b>" b)
(println "op>" op)
(op a b)))
(fn1 [1 + 2])
; a> 1
; b> 2
; op> #object[clojure.core$_PLUS_ 0x5b95b23d clojure.core$_PLUS_#5b95b23d]
(fn1 '(1 + 2))
; a> 1
; b> 2
; op> +
So in the first case, the parenthesis evaluates the clojure.core's function PLUS, which gives the expected (+ 1 2) => 3.
In the second case, you can picture the sequence of forms to be evaluated as an implicit sequence, so call eval on it will give:
(eval ('+ 1 2))
; 2
Which is simply the result of eval-ing each form in the sequence, and so only returns the last value, here 2.
You can think of it as:
(do
'+
1
2)
As hinted in the comment, do is not call.
The '+ is a clojure.lang.Symbol which implements IFn. Thus when calling eval, it will call the java function invoke, with two parameters 1 and 2.
(invoke 1 2) ; is called on +'
Looking at 2 parameters method of invoke for Symbol:
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Symbol.java#L129
We notice it will search for the symbol +' in the first parameter, which should be a map.
(def a {'+ 0})
(apply '+ [a 2])
; 0
When the item (here symbol) cannot be found, the invoke function returns the default value, the second parameter of the invoke function:
(def b { })
(apply '+ [b 2])
; symbol + is not found in b so returning 2
; 2
Playing around with Clojure, I noticed that ('+ 2 2) didn't throw an error like I would've expected--it returns 2. I've spent a few minutes playing around:
(def f (cast clojure.lang.IFn 'a-symbol))
(f 5) ;; => nil
(f 5 5) ;; => 5
(f 5 5 5) ;; ArityException Wrong number of args (3) passed to: Symbol
(f "hey") ;; => nil
(f "foo" "bar") ;; => "bar"
(f "foo" "bar" "baz") ;; => ArityException Wrong number of args (3) passed to: Symbol
As far as I can tell, symbols are being cast to some function with the name Symbol, that takes two arguments and returns the second one. I'm guessing it has something to do with the implementation of the symbol class?
When called as a function symbols (like keywords) look them selves up in the map passed as the second argument
user> (def my-map '{a 1 b 2 c 3})
#'user/my-map
user> ('a my-map)
1
user> ('a my-map :not-found)
1
user> ('z my-map :not-found)
:not-found
and return the third argument, if it was passed, to indicate when nothing was found. In your example when you look up a symbol in something that is not a map, for instance the number 5, it doesn't find it:
user> ('z 4 :not-found)
:not-found
user> ('z 'z :not-found)
:not-found
And returns the third argument, or nil if no third argument was passed.
What's the difference between using "def" to update a var and using "alter-var-root"?
e.g.
(def x 3)
(def x (inc x))
vs
(def x 3)
(alter-var-root #'x inc)
I find alter-var-root very rarely comes up in idiomatic Clojure code; not that there is anything wrong with it, it's just intended for corner cases. If you find yourself using it to build loops and such it's a sign something needs a different approach. I mostly see it in initialization routines for setting access credentials or loggers and such.
alter-var-root uses a function to mechanically change the value of a var while def just sets it to a new value. In your example they are equivalent.
hello.exp> (def foo 4)
#'hello.exp/foo
hello.exp> (alter-var-root #'foo inc)
5
hello.exp> foo
5
alter-var-root is also unwilling to create a new var:
hello.exp> (alter-var-root #'foo1 inc)
CompilerException java.lang.RuntimeException: Unable to resolve var: foo1 in this context, compiling:(NO_SOURCE_PATH:1)
alter-var-root can work on other namespaces as well:
hello.exp> (in-ns 'user)
#<Namespace user>
user> (alter-var-root #'hello.exp/foo inc)
6
user> (def hello.exp/foo 4)
CompilerException java.lang.RuntimeException: Can't create defs outside of current ns, compiling:(NO_SOURCE_PATH:1)
user>
This last use case is the only one I have ever needed in practice. For instance forcing clojure.logging to use the correct slf4j logger as an example from the Pallet project:
(defn force-slf4j
"The repl task brings in commons-logging, which messes up our logging
configuration. This is an attempt to restore sanity."
[]
(binding [*ns* (the-ns 'clojure.tools.logging.slf4j)]
(alter-var-root
#'clojure.tools.logging/*logger-factory*
(constantly (clojure.tools.logging.slf4j/load-factory)))))
Which is just using alter-var-root to reset a var in another namespace regardless of its content on initialization. I suppose it's a bit of a hack ...
alter-var-root provides the added value of being atomic with regards to the function application. Two (possibly concurrent) applications of (alter-var-root #'foo inc) guarantee that foo will increase by 2.
With (def x (inc x)) there is no such guarantee. It might overwrite any changes done by other threads between reading the value of x and writing its updated value.
On the other hand, if you are using alter-var-root for its atomicity then perhaps atoms are better for your use case than vars.
With def:
(def w (vector)) ; create Var named w and bind it to an empty vector
(dotimes [x 9] ; repeat 9 times (keeping iteration number in x):
(future ; execute in other thread:
(def w ; replace root binding of w with
(conj w ; a new vector with all elements from previous (w)
x)))) ; with added an element indicating current iteration (x)
w ; get a value of Var's root binding (identified by symbol w)
; => [0 2 3 6 8 7 4 5] ; 1 is missing !!!
; second thread overlapped with another thread
; during read-conjoin-update and the other thread "won"
With alter-var-root:
(def w (vector)) ; create Var named w and bind it to an empty vector
(dotimes [x 9] ; repeat 9 times (keeping iteration number in x):
(future ; execute in other thread:
(alter-var-root #'w ; atomically alter root binding of w
(fn [old] ; by applying the result of a function,
(conj ; that returns a new vector
old ; containing all elements from previous (w)
x))))) ; with added an element indicating current iteration (x)
w ; get a value of Var's root binding (identified by symbol w)
; => [1 2 4 5 3 0 7 8 6]
(defn get-doc-list [a-term]
(map #(Integer/parseInt %)(take-nth 3 (take (* 3 3)(rest (rest a-term))))))
This function works well on small lists but returns an empty sequence on larger ones. What is the problem?
This is certainly an input issue, as your function will parse at most 3 integers:
user=> (get-doc-list (repeat "1"))
(1 1 1)
And only return empty sequence if less than 3 strings are supplied:
user=> (get-doc-list ["1" "1"])
()
And throw an exception if a non string is given:
user=> (get-doc-list [1 1 1])
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
I suggest checking what the input it "fails" with really is... maybe you are passing in something like [["1" "1" "1" ...]]
user=> (get-doc-list [["1" "1" "1"]])
()
This is a vector of vectors, as you can see nothing is processed so there is no error or sequence to generate.
The issue is your use of the take function, which returns only the first n items of the collection. Regardless of how big a collection you pass get-doc-list, only the first 9 get passed on to take-nth.