Last active
February 4, 2019 17:52
-
-
Save ghadishayban/0ac41e81d4df02ff176c22d16ee8b972 to your computer and use it in GitHub Desktop.
SAM inference helpers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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