damonh
damonh

Reputation: 525

Questions about the source of defmulti

  1. I couldn't help notice that the author prefers calling (first options) multiple times over caching the result:

    [docstring   (if (string? (first options))
                   (first options)
                   nil)
     options     (if (string? (first options))
                   (next options)
                   options)
     m           (if (map? (first options))
                   (first options)
                   {})
     options     (if (map? (first options))
                   (next options)
                   options)
     ...]
    

I assume this is ok (in terms of performance) because it is defining a macro, which will be executed during compilation?

  1. I'm still not sure if I understand the evaluation process of calling a macro. Using (defmulti ...) as an example, correct me if I'm wrong: during compilation, the macro is expanded, of which the result is a call to the special let form, which is in turn translated into bytecode. Then what?

  2. Pretending we are defining a fn instead of a macro, is it worth it to introduce an extra symbol to cache the result of (first options):

    [f           (first options)
     docstring   (if (string? f)
                   f
                   nil)
     options     (if (string? f)
                   (next options)
                   options)
     f           (first options)
     m           (if (map? f)
                   f
                   {})
     options     (if (map? f)
                   (next options)
                   options)
     ...]
    
  3. What exactly does it mean that a var hasRoot? And what is an Unbound?

Upvotes: 1

Views: 63

Answers (1)

Jeremy
Jeremy

Reputation: 22415

  1. Yes, calling first is fast. The varargs are represented as a sequence, specifically a clojure.lang.ArraySeq (at least up until a certain number of arguments).

    user=> ((fn [& args] (type args)) 1 2 3)
    clojure.lang.ArraySeq
    

    The implementation of first() on ArraySeq is an array lookup

    public Object first(){
        if(array != null)
            return array[i];
        return null;
    }
    

    https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/ArraySeq.java#L69

  2. Then the bytecode is then loaded into the JVM and executed.

  3. I doubt that will make a difference.

  4. A var can have thread-locale bindings, meaning it's possible for each thread to see a different value. The root binding is the value the var is initialized with. A thread-locale binding does not squash the root binding, it just shadows it. An Unbound is a implementation optimization for vars that are created without a root binding.

    Take a look at clojure.core/binding for how to set thread-locale values.

Upvotes: 1

Related Questions