Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,23 +154,21 @@ Of course, you can use `defun-` to define a function that is private just as `de

### More Patterns

In fact ,the above `say-hi` function will be expanded to be:
In fact, the above `accum` function can be thought of as expanding to be approximately:

``` clj
(defn
say-hi
{:arglists '([& args])}
[& args#]
(clojure.core.match/match
[(vec args#)]
[[:dennis]]
(do "Hi,good morning, dennis.")
[[:catty]]
(do "Hi, catty, what time is it?")
[[:green]]
(do "Hi,green, what a good day!")
[[other]]
(do (str "Say hi to " other))))
accum
{:arglists '([n] [n ret])}
([-n] (accum n defun.core/placeholder))
([-n -ret]
(clojure.core.match/match [[-n -ret]]
[n defun.core/placeholder]
(do (recur n 0))
[0 ret]
(do ret)
[n ret]
(do (recur (dec n) (+ n ret))))))
```

The argument vector is in fact a pattern in core.match, so we can use all patterns that supported by [core.match](https://github.com/clojure/core.match/wiki/Basic-usage).
Expand Down
24 changes: 22 additions & 2 deletions src/defun/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,26 @@
(map #(cons `fun %) fnspecs)))
~@body)))

(defn- unify-arity-sigs [[k sigs]]
(letfn
[(fold-sig [sigs sig]
(->> sig
(map #(cond-> % (seq? %) first))
(conj sigs)))
(unify-sig-part [& parts]
(let [parts (->> parts (map #(cond-> % (not (symbol? %)) ((fn [_] '_)))))]
(or (some #(when (not= '_ %) %) parts) (first parts))))]
(->> sigs
(reduce fold-sig [])
(apply mapv unify-sig-part))))

(defn- sigs [body]
(->> (@#'clojure.core/sigs body)
vec
(group-by count)
sort
(map unify-arity-sigs)))

#?(:clj
(defmacro defun
"Define a function just like clojure.core/defn, but using core.match to
Expand All @@ -161,8 +181,8 @@
body (if (vector? (first body))
(list body)
body)
name (vary-meta name assoc :arglists (list 'quote (@#'clojure.core/sigs body)))]
`(def ~name (fun ~@body)))))
name (vary-meta name assoc :arglists (list 'quote (sigs body)))]
`(def ~name (fun ~name ~@body)))))

#?(:clj
(defmacro defun-
Expand Down
27 changes: 18 additions & 9 deletions test/defun/core_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
(* x x))
(is (= 4 (square 2)))
(is (= 9 (square 3)))
(is (thrown? IllegalArgumentException (square 3 4)))))
(is (thrown? IllegalArgumentException (square 3 4)))
(is (= '([x]) (-> #'square meta :arglists)))))

(deftest test-say-hi
(testing "say-hi"
Expand All @@ -23,7 +24,8 @@
(is (= "Hi,good morning, dennis." (say-hi :dennis)))
(is (= "Hi, catty, what time is it?" (say-hi :catty)))
(is (= "Hi,green, what a good day!" (say-hi :green)))
(is (= "Say hi to someone" (say-hi "someone")))))
(is (= "Say hi to someone" (say-hi "someone")))
(is (= '([other]) (-> #'say-hi meta :arglists)))))

(deftest test-recursive-function
(testing "accum"
Expand All @@ -32,28 +34,32 @@
([n ret] (recur (dec n) (+ n ret)))
([n] (recur n 0)))
(is (= 6 (accum 3)))
(is (= 5050 (accum 100))))
(is (= 5050 (accum 100)))
(is (= '([n] [n ret]) (-> #'accum meta :arglists))))
(testing "fib"
(defun fib
([0] 0)
([1] 1)
([n] (+ (fib (- n 1)) (fib (- n 2)))))
(is (= 55 (fib 10)))))
(is (= 55 (fib 10)))
(is (= '([n]) (-> #'fib meta :arglists)))))

(deftest test-guards
(testing "funny"
(defun funny
([(N :guard #(= 42 %))] true)
([_] false))
(is (funny 42))
(is (not (funny 43))))
(is (not (funny 43)))
(is (= '([N]) (-> #'funny meta :arglists))))
(testing "valid-geopoint?"
(defun valid-geopoint?
([(_ :guard #(and (> % -180) (< % 180)))
(_ :guard #(and (> % -90) (< % 90)))] true)
([_ _] false))
(is (valid-geopoint? 30 30))
(is (not (valid-geopoint? -181 30)))))
(is (not (valid-geopoint? -181 30)))
(is (= '([_ _]) (-> #'valid-geopoint? meta :arglists)))))

(deftest test-match-literals
(testing "test1"
Expand All @@ -63,7 +69,8 @@
([false true] 3)
([false false] 4))
(is (= 2 (test1 true true)))
(is (= 4 (test1 false false)))))
(is (= 4 (test1 false false)))
(is (= '([_ _]) (-> #'test1 meta :arglists)))))

(deftest test-match-vector
(testing "test3"
Expand All @@ -73,13 +80,15 @@
([[1 2 3]] :a2))
(is (= :a2 (test3 [1 2 3])))
(is (= :a0 (test3 [3 3 2])))
(is (= :a1 (test3 [1 1 3]))))
(is (= :a1 (test3 [1 1 3])))
(is (= '([_]) (-> #'test3 meta :arglists))))
(testing "test2"
(defun test2
([([1] :seq)] :a0)
([([1 2] :seq)] :a1)
([([1 2 nil nil nil] :seq)] :a2))
(is (= :a2 (test2 [1 2 nil nil nil])))))
(is (= :a2 (test2 [1 2 nil nil nil])))
(is (= '([_]) (-> #'test2 meta :arglists)))))

(deftest test-private-macro
(testing "private macro"
Expand Down