Search code examples
clojure

Clojure: Convert a string to a function


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.)


Solution

  • 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);
    }