Replies: 1 comment
-
@Dimagog This library already almost works from source. I cut out the dependency on (require 'babashka.deps)
(babashka.deps/add-deps '{:deps {defun/defun {:mvn/version "0.3.1"}}})
(require '[defun.core :refer [defun]]) (require '[clojure.core.match :refer [match]]
'[clojure.walk :refer [postwalk]])
(def placeholder (Object.))
(defmacro fun*
"Defines a function just like clojure.core/fn with parameter pattern matching
See https://github.com/killme2008/defun for details."
[& sigs]
{:forms '[(fun name? [params* ] exprs*) (fun name? ([params* ] exprs*)+)]}
(let [name (when (symbol? (first sigs)) (first sigs))
sigs (if name (next sigs) sigs)
name (or name (gensym "fn__"))
sigs (if (vector? (first sigs))
(list sigs)
(if (seq? (first sigs))
(let [sig-args (map first sigs)]
(if-some [[form] (some #(if-not (vector? %) [%]) sig-args)]
(throw (IllegalArgumentException.
(if (some? form)
(str "Parameter declaration "
form
" should be a vector")
(str "Parameter declaration missing"))))
sigs))
;; Assume single arity syntax
(throw (IllegalArgumentException.
(if (seq sigs)
(str "Parameter declaration "
(first sigs)
" should be a vector")
"Parameter declaration missing")))))
sigs (sort-by #(count (first %)) sigs)
args (map first sigs)
arities (set (map count args))
max-arity (apply max arities)
other-arities (disj arities max-arity)
placeholder-sym (gensym "placeholder")
placeholder-syms (cons placeholder-sym (repeat '_))
args
(map #(into % (take (- max-arity (count %)) placeholder-syms)) args)
bodies (map #(cons 'do %) (map rest sigs))
bodies (postwalk
(fn [form]
(if (and (list? form) (= 'recur (first form)))
(let [recur-args (next form)
recur-args-n (count recur-args)
to-add-n (- max-arity recur-args-n)
tail (take to-add-n (repeat `placeholder))]
(cons 'recur (concat recur-args tail)))
form))
bodies)
statements (interleave args bodies)
args* (vec (take (count (first args)) (repeatedly gensym)))
main-sig (list
args*
(list
`let [placeholder-sym `placeholder]
(list* `match args* statements)))
other-sigs (map (fn [a]
(let [args (subvec args* 0 a)
to-add-n (- max-arity a)
tail (take to-add-n (repeat `placeholder))]
(list args (cons name (concat args tail)))))
other-arities)
all-sigs (concat other-sigs [main-sig])]
(list* `fn name all-sigs)))
(defn variadic? [sigs]
(->> sigs
(map first)
(map (partial some #{'&}))
(filter some?)
(seq)
(boolean)))
(defmacro fun
"Defines a function just like clojure.core/fn with parameter pattern matching
See https://github.com/killme2008/defun for details."
[& sigs]
{:forms '[(fun name? [params* ] exprs*) (fun name? ([params* ] exprs*)+)]}
(let [name (when (symbol? (first sigs)) (first sigs))
sigs (if name (next sigs) sigs)
sigs (if (vector? (first sigs))
(list sigs)
(if (seq? (first sigs))
(let [sig-args (map first sigs)]
(if-some [[form] (some #(if-not (vector? %) [%]) sig-args)]
(throw (IllegalArgumentException.
(if (some? form)
(str "Parameter declaration "
form
" should be a vector")
(str "Parameter declaration missing"))))
sigs))
;; Assume single arity syntax
(throw (IllegalArgumentException.
(if (seq sigs)
(str "Parameter declaration "
(first sigs)
" should be a vector")
"Parameter declaration missing")))))]
(if (variadic? sigs)
(let [sigs (postwalk
(fn [form]
(if (and (list? form) (= 'recur (first form)))
(list 'recur (cons 'vector (next form)))
form))
sigs)
sigs `([& args#]
(match (vec args#)
~@(mapcat
(fn [[m & more]]
[m (cons 'do more)])
sigs)))]
(list* 'fn (if name
(cons name sigs)
sigs)))
`(fun* ~@sigs))))
(defmacro defun
"Define a function just like clojure.core/defn, but using core.match to
match parameters. See https://github.com/killme2008/defun for details."
[name & body]
(let [body (if (vector? (first body))
(list body)
body)]
`(def ~name (fun ~@body))))
(defun say-hi
([:dennis] "Hi,good morning, dennis.")
([:catty] "Hi, catty, what time is it?")
([:green] "Hi,green, what a good day!")
([other] (str "Say hi to " other)))
(prn (say-hi :dennis))
(prn (say-hi "other")) |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Since you already include
clojure.core.match
, please adddefun
as well: https://github.com/killme2008/defunTogether they allow for very concise function definitions.
Beta Was this translation helpful? Give feedback.
All reactions