Skip to content

Instantly share code, notes, and snippets.

@atw-gr
Last active September 14, 2018 13:55
Show Gist options
  • Save atw-gr/b03cddfe181784e5292b0e3bdb1ed4c0 to your computer and use it in GitHub Desktop.
Save atw-gr/b03cddfe181784e5292b0e3bdb1ed4c0 to your computer and use it in GitHub Desktop.

Keyword applied to set

You know what ({:foo 1} :foo), (:foo {:foo 1}), and (#{:foo} :foo) evaluate to , but what about (:foo #{:foo})?

Answer:

Here a keyword is being used as a function. Is that legal?

(isa? (type :foo) clojure.lang.IFn)
;; => true

Yes, a keyword is a function. Here’s what defines what happens when a keyword is used as a function:

// https://github.com/clojure/clojure/blob/clojure-1.9.0/src/jvm/clojure/lang/Keyword.java#L137

final public Object invoke(Object obj) {
        if(obj instanceof ILookup)
                return ((ILookup)obj).valAt(this);
        return RT.get(obj, this);
}

Is #{:foo} an instance of ILookup?

(isa? (type #{:foo}) clojure.lang.ILookup)
;; => false

It is not, so find what RT.get(obj, this) does when obj is #{:foo} and this is :foo:

// https://github.com/clojure/clojure/blob/clojure-1.9.0/src/jvm/clojure/lang/RT.java#L751

static public Object get(Object coll, Object key){
        if(coll instanceof ILookup)
                return ((ILookup) coll).valAt(key);
        return getFrom(coll, key);
}

static Object getFrom(Object coll, Object key){
        if(coll == null)
                return null;
        else if(coll instanceof Map) {
                Map m = (Map) coll;
                return m.get(key);
        }
        else if(coll instanceof IPersistentSet) {
                IPersistentSet set = (IPersistentSet) coll;
                return set.get(key);
        }
        else if(key instanceof Number && (coll instanceof String || coll.getClass().isArray())) {
                int n = ((Number) key).intValue();
                if(n >= 0 && n < count(coll))
                        return nth(coll, n);
                return null;
        }
        else if(coll instanceof ITransientSet) {
                ITransientSet set = (ITransientSet) coll;
                return set.get(key);
        }

        return null;
}

Once again, #{:foo} is not an ILookup, so getFrom is called. coll is #{:foo}, which is not null, nor is it an instance of Map. It is an instance of IPersistentSet.

The implementation of set.get is in APersistentSet because

(isa? (type #{:foo}) clojure.lang.APersistentSet)
;; => true

and ~APersistentSet~ has an implementation of ~get~:

public Object get(Object key){
        return impl.valAt(key);
}

An instance of APersistentSet has an ~IPersistentMap~, so the above is rougly equivalent to the Clojure:

({:foo :foo} :foo)
;; => :foo

So (:foo #{:foo}) evaluates to :foo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment