Search code examples
clojure

Error while trying to get first element of sequence by index in a function call


I've encountered a problem while I was doing a task from 4clojure.com. Here is the description of a task:

Write a function which returns the last element in a sequence.

I've solved it using the following code:

#(first (reverse %))

When I wanted to change the first function with a number of an index. like so:

#(0 (reverse %))

I've received an error:

java.lang.ClassCastException: java.lang.Long cannot be cast to clojure.lang.IFn

My question is: Why am I receiving this error? I cannot get it, because for instance ([1 2 3 4] 0) is perfectly valid and returns the first element of a sequence so why I cannot use index of an array in the function?

EDIT1: Even the following code does not work and I suppose APersistentVector is first there.

#((reverse %) 0)

EDIT2: I managed to make it work by converting the list which is returned from reverse function to vector. Thanks @Josh

(#((vec (reverse %)) 0)[1 2 3])

Solution

  • If you look at the code for APersistentVector, you will see:

    public abstract class APersistentVector extends AFn ...
    

    AFn implements IFn, which extends java's Callable and Runnable interfaces, which means that a clojure persistent vector can be called as a function, with the argument being used as the index to retrieve. You can see this here:

    public Object invoke(Object arg1) {
        if(Util.isInteger(arg1))
            return nth(((Number) arg1).intValue());
        throw new IllegalArgumentException("Key must be integer");
    }
    

    The same is true for maps and sets; they can all be invoked as functions:

    ({:a 1 :b 2} :b)  ;; 2
    (#{:a :b} :a)     ;; :a
    ([1 2 3 4] 0)     ;; 1
    

    However, a Long (your number zero) does not implement IFn:

    (ancestors (class 42))
    =>
    #{java.lang.Comparable
      java.lang.Number
      java.lang.Object
      java.io.Serializable}
    

    Hence, it cannot be invoked as a function.