Created
January 30, 2017 07:23
-
-
Save solatis/a159559b570ccc226bd00ff87d3bbeca to your computer and use it in GitHub Desktop.
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
(ns specfn | |
(:require | |
[clojure.spec :as s] | |
[clojure.tools.macro :refer [name-with-attributes]] | |
[clojure.walk :refer [postwalk]] | |
[clojure.core.match]) | |
(:refer-clojure :exclude [fn defn])) | |
(s/def ::arglist | |
(s/cat :normal-args (s/* (s/cat :name #(not= '& %) | |
:spec-form (s/? (s/cat :- #{:-} | |
:spec any?)))) | |
:varargs (s/? (s/cat :& #{'&} | |
:name #(not= '& %) | |
:spec-form (s/? (s/cat :- #{:-} | |
:spec any?)))))) | |
(clojure.core/defn parse-arguments | |
"Parses a collection of functions arguments into an easy to navigate | |
structure." | |
[args] | |
(let [{:keys [normal-args varargs]} (s/conform ::arglist args)] | |
(println "normal-args = " (pr-str normal-args)) | |
(println "varargs = " (pr-str varargs)) | |
args)) | |
(defmacro fn [& decl] | |
(let [;; Handle situation of optional names for lambda functions | |
name (when (symbol? (first decl)) | |
(first decl)) | |
decl (if name | |
(next decl) | |
decl) | |
;; | |
decl (if (vector? (first decl)) | |
(list decl) | |
(if (seq? (first decl)) | |
decl | |
;; Assume single arity syntax | |
(throw (IllegalArgumentException. | |
(if (seq decl) | |
(str "Parameter declaration " (first decl) " should be a vector") | |
(str "Parameter declaration missing")))))) | |
;; Blatantly copied from `defun` library, handle recursion | |
decl (postwalk | |
(clojure.core/fn [form] | |
(if (and (list? form) (= 'recur (first form))) | |
(list 'recur (cons 'vector (next form))) | |
form)) | |
decl) | |
;; Apply core.match to all these bitches | |
decl `([& args#] | |
(clojure.core.match/match (vec args#) | |
~@(mapcat | |
(clojure.core/fn [[m & more]] | |
[m (cons 'do more)]) | |
decl)))] | |
(list* 'clojure.core/fn (if name | |
(cons name decl) | |
decl)))) | |
(defmacro defn [name & rest] | |
(let [[name body] (name-with-attributes name rest) | |
body (if (vector? (first body)) | |
(list body) | |
body) | |
name (vary-meta name assoc :argslist (list 'quote (@#'clojure.core/sigs body)))] | |
`(def ~name (fn ~@body)))) | |
(s/def ::foo string?) | |
(s/def ::bar int?) | |
(comment | |
;; High level idea of function declarations | |
(defn fun1 | |
[foo] | |
(println "fun1 is a ::foo " (pr-str foo))) | |
(defn fun2 | |
([foo] | |
(println "fun2 is a ::foo " (pr-str foo))) | |
([1 | |
bar] | |
(println "fun2 is a 1 ::bar " (pr-str bar))) | |
([foo | |
bar] | |
(println "fun2 is a ::foo " (pr-str foo) " ::bar " (pr-str bar)))) | |
(defn fun3 | |
([:foo] | |
(println "fun3 is foo!")) | |
([:bar] | |
(println "fun3 is bar!")) | |
([else] | |
(println "fun3 unrecognized: " (pr-str else))))) | |
(defn fun4 | |
([{:year 2017}] | |
(println "fun4 is 2017")) | |
([{:year ::foo}] | |
(println "fun4 is a string!")) | |
([{:month 1}] | |
(println "fun4 is january")) | |
([({:year year :month month} :guard #(even? (:year %)))] | |
(println "fun4 receives data at " year ", " month))) | |
(comment | |
(fun1 "blah1!") | |
(fun2 "blah2a") | |
(fun2 "blah2a" "blah2b") | |
(fun2 1 "blah2b") | |
(fun3 "blah2b")) | |
(fun4 {:year 2017 :month 1}) | |
(fun4 {:year 2017 :month 1}) | |
(fun4 {:year 2016 :month 1}) | |
(fun4 {:year 2014 :month 5}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment