jank-lang / jank

The native Clojure dialect hosted on LLVM
https://jank-lang.org
Mozilla Public License 2.0
1.69k stars 50 forks source link

Finish multimethods #121

Closed jeaye closed 1 week ago

jeaye commented 3 weeks ago

Currently defmulti and defmethod work. They also support hierarchies. As a demonstration of what works, see these:

Factorial

(defmulti factorial identity)

(defmethod factorial 0 [_]  1)
(defmethod factorial :default [num] 
    (* num (factorial (dec num))))

(factorial 0) ; => 1
(factorial 1) ; => 1
(factorial 3) ; => 6
(factorial 7) ; => 5040

Hierarchies

> (def h (-> (make-hierarchy)
           (derive :foo :bar)))

(defmulti f identity :hierarchy #'h) ;; hierarchy must be a reference type

(defmethod f :default [_] "default")
(defmethod f :bar [_] "bar")
clojure.core/f (multi_function@0x7fb56d6c3f08)
>
(f :unknown)
"default"
> (f :bar)
"bar"
> (f :foo)
"bar"

Compaction

> (defmulti compact map?)

(defmethod compact true [map]
  (into {} (remove (comp nil? second) map)))

(defmethod compact false [col]
  (remove nil? col))
clojure.core/compact (multi_function@0x7fb56d6c3988)
> (compact [:foo 1 nil :bar])
(:foo 1 :bar)
> (compact {:foo 1 :bar nil :baz "hello"})
{:foo 1, :baz "hello"}

What's missing

The following fns after defmethod are not yet implemented (copied from Clojure JVM):

;(defn remove-all-methods
;  "Removes all of the methods of multimethod."
;  {:added "1.2"
;   :static true}
; [^clojure.lang.MultiFn multifn]
; (.reset multifn))
;
;(defn remove-method
;  "Removes the method of multimethod associated with dispatch-value."
;  {:added "1.0"
;   :static true}
; [^clojure.lang.MultiFn multifn dispatch-val]
; (. multifn removeMethod dispatch-val))
;
;(defn prefer-method
;  "Causes the multimethod to prefer matches of dispatch-val-x over dispatch-val-y
;   when there is a conflict"
;  {:added "1.0"
;   :static true}
;  [^clojure.lang.MultiFn multifn dispatch-val-x dispatch-val-y]
;  (. multifn preferMethod dispatch-val-x dispatch-val-y))
;
;(defn methods
;  "Given a multimethod, returns a map of dispatch values -> dispatch fns"
;  {:added "1.0"
;   :static true}
;  [^clojure.lang.MultiFn multifn] (.getMethodTable multifn))
;
;(defn get-method
;  "Given a multimethod and a dispatch value, returns the dispatch fn
;  that would apply to that value, or nil if none apply and no default"
;  {:added "1.0"
;   :static true}
;  [^clojure.lang.MultiFn multifn dispatch-val] (.getMethod multifn dispatch-val))
;
;(defn prefers
;  "Given a multimethod, returns a map of preferred value -> set of other values"
;  {:added "1.0"
;   :static true}
;  [^clojure.lang.MultiFn multifn] (.getPreferTable multifn))

We need to implement them along the same way we have implemented defmethod*. Remove all of the meta maps, since jank doesn't use those for any fns in core right now. Also remove the type hints.

All of this is backed by the multi_function object.

Samy-33 commented 3 weeks ago

This for me.

jeaye commented 1 week ago

Closed by #124.