Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster and more correct eagerization. #9

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/special/core.cljc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
(ns special.core)
(ns special.core
(:require [special.eagerize :as eag]))

(defonce ^:dynamic *-special-condition-handlers-* {})

Expand Down Expand Up @@ -31,7 +32,7 @@
[f]
(fn [& args]
(let [res (apply f args)
_ (pr-str res)]
_ (eag/eagerize res)]
res)))

(defn manage
Expand Down
43 changes: 43 additions & 0 deletions src/special/eagerize.cljc
Original file line number Diff line number Diff line change
@@ -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))))
79 changes: 79 additions & 0 deletions test/special/eagerize_test.clj
Original file line number Diff line number Diff line change
@@ -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))))))