Skip to content

Commit

Permalink
feat: add some more async collection fns
Browse files Browse the repository at this point in the history
  • Loading branch information
k13gomez committed Jan 3, 2024
1 parent 35e3935 commit a113b5a
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
This is a history of changes to k13labs/futurama

# 0.3.5
* Add more async collection fns: `async-map`, `async-some`, `async-every?`

# 0.3.4
* Simplify `async-for` by just executing each iteration inside an async block and then collect after

Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
<groupId>com.github.k13labs</groupId>
<artifactId>futurama</artifactId>
<name>futurama</name>
<version>0.3.4</version>
<version>0.3.5</version>
<scm>
<tag>0.3.4</tag>
<tag>0.3.5</tag>
<url>https://github.com/k13labs/futurama</url>
<connection>scm:git:git://github.com/k13labs/futurama.git</connection>
<developerConnection>scm:git:ssh://git@github.com/k13labs/futurama.git</developerConnection>
Expand Down
39 changes: 39 additions & 0 deletions src/futurama/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,42 @@
(if (nil? ~'outf)
~'outv
(recur ~'more (conj ~'outv (!<! ~'outf)))))))

(defn async-map
"Asynchronously returns the result of applying f to
the set of first items of each coll, followed by applying f to the
set of second items in each coll, until any one of the colls is
exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments."
[f coll & coll-seq]
(let [colls (cons coll coll-seq)
param-seq (->> (apply interleave colls)
(partition (count colls)))]
(async-for
[params param-seq]
(apply f params))))

(defn async-some
"Asynchronously returns the first logical true value of calling pred for any x in coll,
else nil. One common idiom is to use a set as pred, for example
this will return :fred if :fred is in the sequence, otherwise nil:
`(some #{:fred} coll)`"
[pred coll]
(async
(loop [coll coll]
(if-let [res (!<! (pred (first coll)))]
res
(when-let [more (next coll)]
(recur more))))))

(defn async-every?
"Returns true if `(pred x)` is logical true for every x in coll, else false."
[pred coll]
(async
(loop [coll coll]
(if
(nil? (seq coll)) true
(let [res (!<! (pred (first coll)))]
(if-not res
false
(recur (next coll))))))))
41 changes: 40 additions & 1 deletion test/futurama/core_test.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
(ns futurama.core-test
(:require [clojure.test :refer [deftest testing is]]
[futurama.core :refer [!<!! !<! async async-for async? completable-future]]
[futurama.core :refer [!<!! !<! async async-for async-map
async-some async-every?
async? completable-future]]
[clojure.core.async :refer [go timeout put! take! <! >! <!!] :as a]
[criterium.core :refer [report-result
quick-benchmark
Expand All @@ -11,6 +13,43 @@
(def ^:dynamic *test-val1* nil)
(def test-val2 nil)

(deftest async-some-test
(testing "works the same way as a some fn"
(let [state1 (atom 0)
take-fn1 #(swap! state1 inc)
state2 (atom 0)
take-fn2 #(swap! state2 inc)
list1 (repeatedly take-fn1)
list2 (repeatedly take-fn2)]
(is (= 10
(some #{10} list1)
@state1))
(is (= 10
(!<!! (async-some #(async (#{10} %)) list2))
@state2)))))

(deftest async-every-test
(testing "works the same way as a every? fn"
(let [state1 (atom 0)
take-fn1 #(swap! state1 inc)
state2 (atom 0)
take-fn2 #(swap! state2 inc)
list1 (repeatedly take-fn1)
list2 (repeatedly take-fn2)]
(is (false? (every? #(< % 10) list1)))
(is (= 10
@state1))
(is (false? (!<!! (async-every? #(async (< % 10)) list2))))
(is (= 10
@state2)))))

(deftest async-map-test
(testing "works the same way as a map fn with multiple colls"
(let [args (repeat 10 (range 10))]
(is (= [0 10 20 30 40 50 60 70 80 90]
(apply map + args)
(!<!! (apply async-map #(async (apply + %&)) args)))))))

(deftest async-for-test
(testing "can loop for using async ops"
(is (= [[1 1 2 4] [1 3 4 8] [3 1 4 8] [3 3 6 12]]
Expand Down

0 comments on commit a113b5a

Please sign in to comment.