diff --git a/src/special/core.cljc b/src/special/core.cljc index 9c452eb..4170eec 100644 --- a/src/special/core.cljc +++ b/src/special/core.cljc @@ -1,4 +1,5 @@ -(ns special.core) +(ns special.core + (:require [special.eagerize :as eag])) (defonce ^:dynamic *-special-condition-handlers-* {}) @@ -31,7 +32,7 @@ [f] (fn [& args] (let [res (apply f args) - _ (pr-str res)] + _ (eag/eagerize res)] res))) (defn manage diff --git a/src/special/eagerize.cljc b/src/special/eagerize.cljc new file mode 100644 index 0000000..9f9f9ee --- /dev/null +++ b/src/special/eagerize.cljc @@ -0,0 +1,43 @@ +(ns special.eagerize) + +#?(:clj + (defonce array-object-type (delay (Class/forName "[Ljava.lang.Object;")))) + +(defprotocol Eagerizable + "Container types that want to work safely with special should implement this + protocol in a way where all elements they contain, even deeply nested, will + be realized and made eager." + (eagerize [this])) + +#?(:clj + (extend-protocol Eagerizable + nil + (eagerize [this] this) + java.lang.Object + (eagerize [this] (if (instance? @array-object-type this) + (doall (map eagerize this)) + this)) + clojure.lang.IPersistentList + (eagerize [this] (apply list (map eagerize this))) + clojure.lang.IMapEntry + (eagerize [this] (vec (map eagerize this))) + clojure.lang.ISeq + (eagerize [this] (doall (map eagerize this))) + clojure.lang.IRecord + (eagerize [this] (reduce (fn [r x] (conj r (eagerize x))) this this)) + clojure.lang.IPersistentCollection + (eagerize [this] (into (empty this) (map eagerize this))) + clojure.lang.IType + (eagerize [this] (doall + (map #(eagerize (.get % this)) + (.getFields (class this))))) + clojure.lang.Delay + (eagerize [this] (eagerize (deref this))) + java.lang.Iterable + (eagerize [this] (doall (map eagerize this))) + java.util.Map + (eagerize [this] (doall (map eagerize (.values this)))))) + +#?(:cljs (extend-protocol Eagerizable + default + (eagerize [this] (pr-str this)))) diff --git a/test/special/eagerize_test.clj b/test/special/eagerize_test.clj new file mode 100644 index 0000000..d8e36dc --- /dev/null +++ b/test/special/eagerize_test.clj @@ -0,0 +1,79 @@ +(ns special.eagerize-test + (:require [clojure.test :refer :all] + [special.eagerize :refer :all])) + +(defn- make-nested-lazy-list + "Returns a lazy-sequece of e. + Defaults to random-ints when called with no args." + ([] + (make-nested-lazy-list #(rand-int 42))) + ([e] + (repeatedly 10 (constantly e)))) + +(defrecord TestRecord [s]) +(deftype TestType [s]) + +(deftest eagerize-test + (testing "Can eagerize deep nested Clojure IPersistentList." + (is (realized? + (let [ls (list (make-nested-lazy-list))] + (eagerize ls) + (first ls))))) + + (testing "Can eagerize deep nested Clojure IMapEntry." + (is (realized? + (let [ls (first {:e (make-nested-lazy-list)})] + (eagerize ls) + (val ls))))) + + (testing "Can eagerize deep nested Clojure ISeq." + (is (realized? + (let [ls (make-nested-lazy-list (make-nested-lazy-list))] + (eagerize ls) + (first ls))))) + + (testing "Can eagerize deep nested Clojure IRecord." + (is (realized? + (let [ls (->TestRecord (make-nested-lazy-list))] + (eagerize ls) + (:s ls))))) + + (testing "Can eagerize deep nested Clojure IType." + (is (realized? + (let [ls (TestType. (make-nested-lazy-list))] + (eagerize ls) + (.-s ls))))) + + (testing "Can eagerize deep nested Clojure Delay." + (is (realized? + (let [ls (delay (make-nested-lazy-list))] + (eagerize ls) + @ls)))) + + (testing "Can eagerize deep nested Java Iterable." + (is (realized? + (let [ls (doto (java.util.LinkedList.) + (.add (make-nested-lazy-list)))] + (eagerize ls) + (first ls))))) + + (testing "Can eagerize deep nested Java AbstractMap." + (is (realized? + (let [ls (doto (java.util.HashMap.) + (.put "a" (make-nested-lazy-list)))] + (eagerize ls) + (.get ls "a"))))) + + (testing "Can eagerize deep nested Java Stack." + (is (realized? + (let [ls (doto (java.util.Stack.) + (.push (make-nested-lazy-list)))] + (eagerize ls) + (.pop ls))))) + + (testing "Can eagerize deep nested Java Arrays." + (is (realized? + (let [ls (doto (make-array clojure.lang.ISeq 2) + (aset 0 (make-nested-lazy-list)))] + (eagerize ls) + (aget ls 0))))))