Creating a cljc `atom?` function


#1

I’m creating a cljc function that needs to dispatch based on whether one of its args is an atom. This SO answer suggests:
(defn atom? (instance? clojure.lang.IAtom x))

The answer is old and that answer wouldn’t work in cljs, anyway. Is there a simple way to get both? preferably without reader conditionals?


#2

Here’s what I came up with:

(defn atom?
  [a]
  (try (do (deref a) true)
       (catch #?(:clj Exception :cljs js/Error) _ false)))

It seems ok so far; a pity there isn’t an atom? in clojure.core…


#3

It will give you false positives for lots of things: refs, futures, delays… Also may block for a long time if it’s a promise. I personally see no problems with using reader conditionals:

(defn atom? [x]
  #?(:clj (instance? clojure.lang.IAtom x)
     :cljs (instance? cljs.core.IAtom x)))

(I didn’t try to verify that code works, was judging by the source, so it may be wrong)


#4

@vlaaad’s solution should work. Maria doesn’t support reader conditionals yet but you can verify the cljs part:

https://www.maria.cloud/gist/b399ac08347af13c6d6eead58b25e3c7?eval=true


#5

FYI from the office IP I’m being rate limited so I can’t get at that gist


#6

@pesterhazy Hmm. Does it work if you sign in via GitHub (on maria)?


#7

Now it works, even without signing in

I also tried signing in on maria - that works too


#8

Thanks all! Trying the solution @vlaad gave, I realized I’d forgotten an important corollary: I was in CLJC but I needed to also get “true” on reformation.core/atom. In this case, the duck-typing I put earlier worked ok (since I couldn’t find any common ground between reagent atoms and other atoms). This is part of a library in a fairly constrained domain, so I’m not too worried about non-atom de-reffables (actually, I probably don’t know many of those).


#9

When I hit the same problem, I eventually figured out that I don’t really need to know if something is an atom, but whether it is derefable. So I use this:

(defn derefable? [x]
  (satisfies? #?(:cljs IDeref :clj clojure.lang.IDeref) x))

#10

Ah! Same working logic as my solution, but cleaner without the try-catch. I’ve never used satisfies? before. Thanks!


#11

Speculative has this spec for an atom:

(s/def ::atom
  (s/with-gen
    (fn [a]
      #?(:clj (instance? clojure.lang.IAtom a)
         :cljs (satisfies? IAtom a)))
    #(gen/fmap (fn [any]
                 (atom any))
               (s/gen ::any))))

source

You can extract the anonymous function predicate if you need it.