Skip to content

Instantly share code, notes, and snippets.

@ghadishayban
Last active February 4, 2019 17:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ghadishayban/0ac41e81d4df02ff176c22d16ee8b972 to your computer and use it in GitHub Desktop.
Save ghadishayban/0ac41e81d4df02ff176c22d16ee8b972 to your computer and use it in GitHub Desktop.
SAM inference helpers
(import '[java.lang.reflect Method Modifier])
(set! *warn-on-reflection* true)
(defn- samsig
"Given a SAM interface, returns the j.l.reflect.Method of the abstract method"
[sym]
(let [kls (resolve sym)]
(if (and (class? kls) (.isInterface ^Class kls))
(let [mid (fn [^Method m] [(.getName m) (vec (.getParameterTypes m))])
Object-mask (into #{} (map mid) (.getMethods java.lang.Object))
ms (->> (.getMethods ^Class kls)
(filter #(Modifier/isAbstract (.getModifiers ^Method %)))
(remove (comp Object-mask mid)))]
(when (next ms)
(throw (ex-info "not a SAM, too many abstract methods" {:interface kls :methods ms})))
(first ms))
(throw (ex-info "not an interface" {:sym sym})))))
(defmacro reify-SAM
[interf argvec & body]
(let [maybe-destructured #'clojure.core/maybe-destructured
^Method m (samsig interf)
argvec (vec (cons (gensym "this") argvec))
args+body (maybe-destructured argvec body)]
(when (not= (count argvec) (inc (.getParameterCount m)))
(throw (ex-info "arg count mismatch" {:method m :argvec argvec})))
`(reify ~interf
~(cons (symbol (.getName m)) args+body))))
(defmacro jfn
[interf fnexpr]
(let [^Method m (samsig interf)
args (repeatedly (.getParameterCount m) gensym)
fsym (gensym "fn")]
`(let [~fsym ~fnexpr]
(reify ~interf
~(list (symbol (.getName m))
(vec (cons (gensym "this") args))
(cons fsym args))))))
(comment
;; this gist enables you to participate in a SAM, or adapt an IFn to a SAM
;; do not comment on names until Rich decides this is a good idea
;; participate
(reify-SAM java.util.function.Predicate
[v]
(even? v))
;; adapt IFn
(.. [1 2 3 4]
(stream)
(filter (jfn java.util.function.Predicate even?))
(collect (java.util.stream.Collectors/toList)))
;; [2 4]
;; edge case where Comparator re-overrides Object.equals() and marks it abstract, per JLS
;; must ignore methods from Object
(reify-SAM java.util.Comparator
[x y]
(and (vector? x) (vector? y)
(compare (count x) (count y)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment