Skip to content

Instantly share code, notes, and snippets.

@solatis
Created January 30, 2017 07:23
Show Gist options
  • Save solatis/a159559b570ccc226bd00ff87d3bbeca to your computer and use it in GitHub Desktop.
Save solatis/a159559b570ccc226bd00ff87d3bbeca to your computer and use it in GitHub Desktop.
(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