hy/hy/contrib/multi.hy
Kodi Arfer 18acfe6495 Revert the extension of with-decorator to setv
This is no longer necessary now that `defn` always produces a `FunctionDef`.

To compensate, I've made small edits to two contrib modules and reverted a small test change.
2017-04-13 06:36:00 +03:00

114 lines
4.4 KiB
Hy

;; Hy Arity-overloading
;; Copyright (c) 2014 Morten Linderud <mcfoxax@gmail.com>
;; Copyright (c) 2016 Tuukka Turto <tuukka.turto@oktaeder.net>
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"),
;; to deal in the Software without restriction, including without limitation
;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
;; and/or sell copies of the Software, and to permit persons to whom the
;; Software is furnished to do so, subject to the following conditions:
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
;; DEALINGS IN THE SOFTWARE.
(import [collections [defaultdict]]
[hy [HyExpression HyList HyString]])
(defclass MultiDispatch [object] [
_fns (defaultdict dict)
__init__ (fn [self f]
(setv self.f f)
(setv self.__doc__ f.__doc__)
(unless (in f.__name__ (.keys (get self._fns f.__module__)))
(setv (get self._fns f.__module__ f.__name__) {}))
(setv values f.__code__.co_varnames)
(setv (get self._fns f.__module__ f.__name__ values) f))
fn? (fn [self v args kwargs]
"Compare the given (checked fn) to the called fn"
(setv com (+ (list args) (list (.keys kwargs))))
(and
(= (len com) (len v))
(.issubset (frozenset (.keys kwargs)) com)))
__call__ (fn [self &rest args &kwargs kwargs]
(setv output None)
(for [[i f] (.items (get self._fns self.f.__module__ self.f.__name__))]
(when (.fn? self i args kwargs)
(setv output (apply f args kwargs))
(break)))
(if output
output
(raise (TypeError "No matching functions with this signature"))))])
(defn multi-decorator [dispatch-fn]
(setv inner (fn [&rest args &kwargs kwargs]
(setv dispatch-key (apply dispatch-fn args kwargs))
(if (in dispatch-key inner.--multi--)
(apply (get inner.--multi-- dispatch-key) args kwargs)
(apply inner.--multi-default-- args kwargs))))
(setv inner.--multi-- {})
(setv inner.--doc-- dispatch-fn.--doc--)
(setv inner.--multi-default-- (fn [&rest args &kwargs kwargs] None))
inner)
(defn method-decorator [dispatch-fn &optional [dispatch-key None]]
(setv apply-decorator
(fn [func]
(if (is dispatch-key None)
(setv dispatch-fn.--multi-default-- func)
(assoc dispatch-fn.--multi-- dispatch-key func))
dispatch-fn))
apply-decorator)
(defmacro defmulti [name params &rest body]
`(do (import [hy.contrib.multi [multi-decorator]])
(with-decorator multi-decorator
(defn ~name ~params ~@body))))
(defmacro defmethod [name multi-key params &rest body]
`(do (import [hy.contrib.multi [method-decorator]])
(with-decorator (method-decorator ~name ~multi-key)
(defn ~name ~params ~@body))))
(defmacro default-method [name params &rest body]
`(do (import [hy.contrib.multi [method-decorator]])
(with-decorator (method-decorator ~name)
(defn ~name ~params ~@body))))
(defn head-tail [l]
(, (get l 0) (cut l 1)))
(defmacro defn [name &rest bodies]
(def arity-overloaded? (fn [bodies]
(if (isinstance (first bodies) HyString)
(arity-overloaded? (rest bodies))
(isinstance (first bodies) HyExpression))))
(if (arity-overloaded? bodies)
(do
(def comment (HyString))
(if (= (type (first bodies)) HyString)
(def [comment bodies] (head-tail bodies)))
(def ret `(do))
(.append ret '(import [hy.contrib.multi [MultiDispatch]]))
(for [body bodies]
(def [let-binds body] (head-tail body))
(.append ret
`(with-decorator MultiDispatch (defn ~name ~let-binds ~comment ~@body))))
ret)
(do
(setv [lambda-list body] (head-tail bodies))
`(setv ~name (fn* ~lambda-list ~@body)))))