|
18 | 18 |
|
19 | 19 | (import* attr)
|
20 | 20 |
|
| 21 | +(import* basilisp.lang.multifn) |
| 22 | + |
21 | 23 | (def ^{:doc "Create a list from the arguments."
|
22 | 24 | :arglists '([& args])}
|
23 | 25 | list
|
|
1414 | 1416 | [x]
|
1415 | 1417 | (= 0 x))
|
1416 | 1418 |
|
1417 |
| -;;;;;;;;;;;;;;;;;;; |
1418 |
| -;; Type Coercion ;; |
1419 |
| -;;;;;;;;;;;;;;;;;;; |
| 1419 | +;;;;;;;;;;;;;;;;;;;; |
| 1420 | +;; Type Utilities ;; |
| 1421 | +;;;;;;;;;;;;;;;;;;;; |
| 1422 | + |
| 1423 | +(defn bases |
| 1424 | + "Return the direct superclasses and interfaces of cls as a sequence." |
| 1425 | + [cls] |
| 1426 | + (apply list (.-__bases__ cls))) |
1420 | 1427 |
|
1421 | 1428 | (defn cast
|
1422 | 1429 | "Throws a `TypeError` if x is not a cls. Otherwise, return x."
|
|
1431 | 1438 | [x]
|
1432 | 1439 | (python/type x))
|
1433 | 1440 |
|
| 1441 | +(defn subclasses |
| 1442 | + "Return a set of subclasses of cls. |
| 1443 | + |
| 1444 | + This set is not guaranteed to be exhaustive because classes only retain a |
| 1445 | + weak reference to their subclasses, so it is possible subclasses of cls |
| 1446 | + have been garbage collected." |
| 1447 | + [cls] |
| 1448 | + (set (.__subclasses__ cls))) |
| 1449 | + |
| 1450 | +(defn supers |
| 1451 | + "Return the direct and indirect superclasses and interfaces of cls as a set." |
| 1452 | + [cls] |
| 1453 | + (set (python/filter #(not= cls %) (.-__mro__ cls)))) |
| 1454 | + |
1434 | 1455 | (defn type
|
1435 | 1456 | "Return the type of x."
|
1436 | 1457 | [x]
|
1437 | 1458 | (python/type x))
|
1438 | 1459 |
|
| 1460 | +;;;;;;;;;;;;;;;;;;; |
| 1461 | +;; Type Coercion ;; |
| 1462 | +;;;;;;;;;;;;;;;;;;; |
| 1463 | + |
1439 | 1464 | (defn bigdec
|
1440 | 1465 | "Coerce x to a Decimal."
|
1441 | 1466 | [x]
|
|
2040 | 2065 | "Return a new version of the set s without the given elements. If the elements
|
2041 | 2066 | don't exist in s, they are ignored."
|
2042 | 2067 | ([s] s)
|
2043 |
| - ([s & elems] |
2044 |
| - (apply (.-disj s) elems))) |
| 2068 | + ([s elem] |
| 2069 | + (when s |
| 2070 | + (.disj s elem))) |
| 2071 | + ([s elem & elems] |
| 2072 | + (when s |
| 2073 | + (apply (.-disj s) elem elems)))) |
2045 | 2074 |
|
2046 | 2075 | (defn dissoc
|
2047 | 2076 | "Return a new version of m without the given keys. If the keys
|
2048 | 2077 | don't exist in m, they are ignored."
|
2049 |
| - [m & ks] |
2050 |
| - (apply (.-dissoc m) ks)) |
| 2078 | + ([m] m) |
| 2079 | + ([m k] |
| 2080 | + (when m |
| 2081 | + (.dissoc m k))) |
| 2082 | + ([m k & ks] |
| 2083 | + (when m |
| 2084 | + (apply (.-dissoc m) k ks)))) |
2051 | 2085 |
|
2052 | 2086 | (defn get
|
2053 | 2087 | "Return the entry of `m` corresponding to `k` if it exists or `nil`/`default`
|
|
4593 | 4627 | [pattern s]
|
4594 | 4628 | (lazy-re-seq (seq (re/finditer pattern s))))
|
4595 | 4629 |
|
| 4630 | +;;;;;;;;;;;;;;;;; |
| 4631 | +;; Hierarchies ;; |
| 4632 | +;;;;;;;;;;;;;;;;; |
| 4633 | + |
| 4634 | +(defn make-hierarchy |
| 4635 | + "Return a hierarchy that may be used to establish parent and child |
| 4636 | + relationships via `derive` (relationships may be removed via `underive`). |
| 4637 | + |
| 4638 | + Relationships can be queried using `ancestors`, `descendants`, `parents`, and |
| 4639 | + `isa?`." |
| 4640 | + [] |
| 4641 | + {:parents {} |
| 4642 | + :ancestors {} |
| 4643 | + :descendants {}}) |
| 4644 | + |
| 4645 | +(def ^:private ^:redef global-hierarchy (make-hierarchy)) |
| 4646 | + |
| 4647 | +(defn ancestors |
| 4648 | + "Return the set of all ancestors (parents and grandparents and so forth) of |
| 4649 | + `tag` in hierarchy `h`. |
| 4650 | + |
| 4651 | + `tag` may be either a valid Python type or a namespace-qualified keyword or |
| 4652 | + symbol. If `tag` is a Python type, ancestors include any relationships |
| 4653 | + established via calls to `derive` as well as any superclasses (as returned by |
| 4654 | + `supers`). If `tag` is a namespace-qualified keyword, only relationships from |
| 4655 | + `derive` are returned. |
| 4656 | + |
| 4657 | + `h` must be a hierarchy returned by `make-hierarchy`. If `h` is not supplied, |
| 4658 | + the global hierarchy will be used." |
| 4659 | + ([tag] |
| 4660 | + (ancestors global-hierarchy tag)) |
| 4661 | + ([h tag] |
| 4662 | + (not-empty |
| 4663 | + (let [hierarchy-ancestors (get-in h [:ancestors tag] #{})] |
| 4664 | + (if (class? tag) |
| 4665 | + (reduce conj hierarchy-ancestors (supers tag)) |
| 4666 | + hierarchy-ancestors))))) |
| 4667 | + |
| 4668 | +(defn descendants |
| 4669 | + "Return the set of all descendants (children and grandchildren and so forth) |
| 4670 | + of `tag` in hierarchy `h`. |
| 4671 | + |
| 4672 | + `tag` must be a namespace-qualified keyword or symbol. Python types are not |
| 4673 | + supported for `descendants` checks. |
| 4674 | + |
| 4675 | + `h` must be a hierarchy returned by `make-hierarchy`. If `h` is not supplied, |
| 4676 | + the global hierarchy will be used." |
| 4677 | + ([tag] |
| 4678 | + (descendants global-hierarchy tag)) |
| 4679 | + ([h tag] |
| 4680 | + (not-empty |
| 4681 | + (let [hierarchy-ancestors (get-in h [:descendants tag] #{})] |
| 4682 | + (if (class? tag) |
| 4683 | + (throw (python/TypeError "Cannot get descendants of classes")) |
| 4684 | + hierarchy-ancestors))))) |
| 4685 | + |
| 4686 | +(defn parents |
| 4687 | + "Return the set of all direct parents of `tag` in hierarchy `h`. |
| 4688 | + |
| 4689 | + `tag` may be either a valid Python type or a namespace-qualified keyword or |
| 4690 | + symbol. If `tag` is a Python type, ancestors include any relationships |
| 4691 | + established via calls to `derive` as well as any immediate superclasses (as |
| 4692 | + returned by `bases`). If `tag` is a namespace-qualified keyword, only |
| 4693 | + relationships from `derive` are returned. |
| 4694 | + |
| 4695 | + `h` must be a hierarchy returned by `make-hierarchy`. If `h` is not supplied, |
| 4696 | + the global hierarchy will be used." |
| 4697 | + ([tag] |
| 4698 | + (parents global-hierarchy tag)) |
| 4699 | + ([h tag] |
| 4700 | + (not-empty |
| 4701 | + (let [hierarchy-ancestors (get-in h [:parents tag] #{})] |
| 4702 | + (if (class? tag) |
| 4703 | + (reduce conj hierarchy-ancestors (bases tag)) |
| 4704 | + hierarchy-ancestors))))) |
| 4705 | + |
| 4706 | +(defn isa? |
| 4707 | + "Return true if `tag` is equal to `parent` or is a descendant of `parent` in |
| 4708 | + hierarchy `h`. |
| 4709 | + |
| 4710 | + Both `tag` and `parent` may be a valid Python type or a namespace-qualified |
| 4711 | + keyword or symbol or a vector of namespace-qualified keywords or symbols. If |
| 4712 | + either of `tag` or `parent` is a vector, the other must be a vector as well. |
| 4713 | + |
| 4714 | + Ancestors will be fetched for `tag` using `ancestors`. |
| 4715 | + |
| 4716 | + `h` must be a hierarchy returned by `make-hierarchy`. If `h` is not supplied, |
| 4717 | + the global hierarchy will be used." |
| 4718 | + ([tag parent] |
| 4719 | + (isa? global-hierarchy tag parent)) |
| 4720 | + ([h tag parent] |
| 4721 | + (or (= tag parent) |
| 4722 | + (and (vector? tag) |
| 4723 | + (vector? parent) |
| 4724 | + (->> (map (partial isa? h) tag parent) |
| 4725 | + (every? identity))) |
| 4726 | + (contains? (ancestors h tag) parent)))) |
| 4727 | + |
| 4728 | +(defn derive |
| 4729 | + "Derive a parent/child relationship between `tag` and `parent`. |
| 4730 | + |
| 4731 | + `tag` may be either a valid Python type or a namespace-qualified keyword or |
| 4732 | + symbol. |
| 4733 | + |
| 4734 | + `parent` must be a namespace-qualified keyword or symbol. |
| 4735 | + |
| 4736 | + `h` must be a hierarchy returned by `make-hierarchy`. If `h` is not supplied, |
| 4737 | + the global hierarchy will be used. |
| 4738 | + |
| 4739 | + Relationships may be removed via `underive`." |
| 4740 | + ([tag parent] |
| 4741 | + (alter-var-root #'global-hierarchy derive tag parent)) |
| 4742 | + ([h tag parent] |
| 4743 | + (assert (qualified-ident? parent) "Parent must be a qualified keyword or symbol") |
| 4744 | + (assert (or (qualified-ident? tag) |
| 4745 | + (class? tag)) |
| 4746 | + "Tag must be a valid Python type or a qualified keyword or symbol") |
| 4747 | + |
| 4748 | + (let [parent-ancestors (get-in h [:ancestors parent] #{}) |
| 4749 | + cur-descendants (get-in h [:descendants tag] #{})] |
| 4750 | + {:parents (as-> (get-in h [:parents tag] #{}) $ |
| 4751 | + (conj $ parent) |
| 4752 | + (assoc (:parents h) tag $)) |
| 4753 | + :ancestors (reduce (fn [ancestors descendant] |
| 4754 | + (->> (get ancestors descendant) |
| 4755 | + (apply conj parent-ancestors parent) |
| 4756 | + (set) |
| 4757 | + (assoc ancestors descendant))) |
| 4758 | + (:ancestors h) |
| 4759 | + (conj cur-descendants tag)) |
| 4760 | + :descendants (reduce (fn [descendants ancestor] |
| 4761 | + (->> (get descendants ancestor) |
| 4762 | + (apply conj cur-descendants tag) |
| 4763 | + (set) |
| 4764 | + (assoc descendants ancestor))) |
| 4765 | + (:descendants h) |
| 4766 | + (conj parent-ancestors parent))}))) |
| 4767 | + |
| 4768 | +(defn underive |
| 4769 | + "Remove a parent/child relationship between `tag` and `parent` originally |
| 4770 | + created via `derive`. |
| 4771 | + |
| 4772 | + `tag` may be either a valid Python type or a namespace-qualified keyword or |
| 4773 | + symbol. |
| 4774 | + |
| 4775 | + `parent` must be a namespace-qualified keyword or symbol. |
| 4776 | + |
| 4777 | + `h` must be a hierarchy returned by `make-hierarchy`. If `h` is not supplied, |
| 4778 | + the global hierarchy will be used." |
| 4779 | + ([tag parent] |
| 4780 | + (alter-var-root #'global-hierarchy underive tag parent)) |
| 4781 | + ([h tag parent] |
| 4782 | + (assert (qualified-ident? parent) "Parent must be a qualified keyword or symbol") |
| 4783 | + (assert (or (qualified-ident? tag) |
| 4784 | + (class? tag)) |
| 4785 | + "Tag must be a valid Python type or a qualified keyword or symbol") |
| 4786 | + |
| 4787 | + (let [tag-parents (-> (get-in h [:parents tag] #{}) |
| 4788 | + (disj parent)) |
| 4789 | + new-parents (if (seq tag-parents) |
| 4790 | + (assoc (:parents h) tag tag-parents) |
| 4791 | + (dissoc (:parents h) tag))] |
| 4792 | + (->> new-parents |
| 4793 | + (mapcat (fn [pair] |
| 4794 | + (let [tag (first pair) |
| 4795 | + parents (second pair)] |
| 4796 | + (map #(vector tag %) parents)))) |
| 4797 | + (reduce (fn [h pair] |
| 4798 | + (derive h (first pair) (second pair))) |
| 4799 | + (make-hierarchy)))))) |
| 4800 | + |
4596 | 4801 | ;;;;;;;;;;;;;;;;;;
|
4597 | 4802 | ;; Multimethods ;;
|
4598 | 4803 | ;;;;;;;;;;;;;;;;;;
|
4599 | 4804 |
|
4600 |
| -(import* basilisp.lang.multifn) |
4601 |
| - |
4602 | 4805 | (defmacro defmulti
|
4603 | 4806 | "Define a new multimethod with the dispatch function."
|
4604 | 4807 | [name & body]
|
|
0 commit comments