Skip to content

Commit

Permalink
Merge pull request #41 from nubank/rm-promesa-as-default-applicative-…
Browse files Browse the repository at this point in the history
…engine

Remove the default engine behavior in internal applicative eval functions
  • Loading branch information
joaopluigi authored Nov 18, 2024
2 parents ed9624c + ebc2fa6 commit ce2598f
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 44 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 2.0.2 / 2024-11-14
- Remove the default engine behavior in internal applicative eval functions

## 2.0.1 / 2024-11-07
- Allow implicit args for nodely syntax macros (e.g. >leaf) to include namespaces in the implicit arg symbols.

Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject dev.nu/nodely "2.0.1"
(defproject dev.nu/nodely "2.0.2"
:description "Decoupling data fetching from data dependency declaration"
:url "https://github.com/nubank/nodely"
:license {:name "MIT"}
Expand Down
47 changes: 18 additions & 29 deletions src/nodely/engine/applicative.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
[clojure.pprint :as pp]
[nodely.data :as data]
[nodely.engine.applicative.core :as app]
[nodely.engine.applicative.promesa :as promesa]
[nodely.engine.applicative.protocols :as protocols]
[nodely.engine.core :as core]
[nodely.engine.lazy-env :as lazy-env]))
Expand Down Expand Up @@ -75,37 +74,27 @@
(validator node-ret node)))

(defn eval-key
([env k]
(eval-key env k {}))
([env k opts]
(let [opts (merge {::context promesa/context} opts)
lazy-env (lazy-env/lazy-env env eval-in-context opts)]
(::data/value (protocols/-extract (get lazy-env k))))))
[env k opts]
(let [lazy-env (lazy-env/lazy-env env eval-in-context opts)]
(::data/value (protocols/-extract (get lazy-env k)))))

(defn eval-key-contextual
([env k]
(eval-key env k {}))
([env k opts]
(let [opts (merge {::context promesa/context} opts)
lazy-env (lazy-env/lazy-env env eval-in-context opts)]
(app/fmap ::data/value (get lazy-env k)))))
[env k opts]
(let [lazy-env (lazy-env/lazy-env env eval-in-context opts)]
(app/fmap ::data/value (get lazy-env k))))

(defn eval-key-channel
([env k]
(eval-key-channel env k {}))
([env k opts]
(let [contextual-v (eval-key-contextual env k opts)
chan (async/promise-chan)]
(app/fmap (partial async/put! chan) contextual-v)
chan)))
[env k opts]
(let [contextual-v (eval-key-contextual env k opts)
chan (async/promise-chan)]
(app/fmap (partial async/put! chan) contextual-v)
chan))

(defn eval
([env k]
(eval env k {::context promesa/context}))
([env k opts]
(let [lazy-env (lazy-env/lazy-env env eval-in-context opts)]
(protocols/-extract (get lazy-env k)) ;; ensures k is resolved
(merge env
(reduce (fn [acc [k v]] (assoc acc k (protocols/-extract v)))
{}
(lazy-env/scheduled-nodes lazy-env))))))
[env k opts]
(let [lazy-env (lazy-env/lazy-env env eval-in-context opts)]
(protocols/-extract (get lazy-env k)) ;; ensures k is resolved
(merge env
(reduce (fn [acc [k v]] (assoc acc k (protocols/-extract v)))
{}
(lazy-env/scheduled-nodes lazy-env)))))
31 changes: 17 additions & 14 deletions test/nodely/engine/applicative_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@

(deftest eval-key-test
(testing "eval promise"
(is (match? 3 (applicative/eval-key test-env :c))))
(is (match? 3 (applicative/eval-key test-env :c {::applicative/context promesa/context}))))
(testing "async works"
(let [[time-ns result] (criterium/time-body (applicative/eval-key test-env+delay :d))]
(let [[time-ns result] (criterium/time-body (applicative/eval-key test-env+delay :d {::applicative/context promesa/context}))]
(is (match? {:a 3 :b 6 :c 9} result))
(is (match? (matchers/within-delta 100000000 1000000000) time-ns))))
(testing "tricky example"
(is (match? 4 (applicative/eval-key tricky-example :z)))))
(is (match? 4 (applicative/eval-key tricky-example :z {::applicative/context promesa/context})))))

(deftest eval-key-test-core-async
(testing "eval promise"
Expand All @@ -150,7 +150,7 @@
(is (match? {:a {::data/value 2}
:b {::data/value 1}
:c {::data/value 3}}
(applicative/eval test-env :c))))
(applicative/eval test-env :c {::applicative/context promesa/context}))))
(testing "tricky example"
(is (match? {:x (data/value 1)
:y (data/value 2)
Expand All @@ -160,29 +160,29 @@
:w (data/value 4)
:z {::data/type :leaf
::data/inputs #{:w}}}
(applicative/eval tricky-example :w)))))
(applicative/eval tricky-example :w {::applicative/context promesa/context})))))

(deftest eval-env-with-sequence
(testing "async response is equal to sync response"
(is (match? (-> (core/resolve :b env-with-sequence) (get :b) ::data/value)
(applicative/eval-key env-with-sequence :b))))
(applicative/eval-key env-with-sequence :b {::applicative/context promesa/context}))))
(testing "sync=async for sequence with nil values"
(is (match? (-> (core/resolve :b env+sequence-with-nil-values) (get :b) ::data/value)
(applicative/eval-key env+sequence-with-nil-values :b))))
(applicative/eval-key env+sequence-with-nil-values :b {::applicative/context promesa/context}))))
(testing "sync=async for sequence returning nil values"
(is (match? (-> (core/resolve :b env+sequence-returning-nil-values) (get :b) ::data/value)
(applicative/eval-key env+sequence-returning-nil-values :b))))
(applicative/eval-key env+sequence-returning-nil-values :b {::applicative/context promesa/context}))))
(testing "async version takes a third of the time of sync version
(runtime diff is 2 sec, within a tolerance of 3ms"
(let [[nanosec-sync _] (criterium/time-body (core/resolve :c env-with-sequence+delay-sync))
[nanosec-async _] (criterium/time-body (applicative/eval-key env-with-sequence+delay :c))]
[nanosec-async _] (criterium/time-body (applicative/eval-key env-with-sequence+delay :c {::applicative/context promesa/context}))]
(is (match? (matchers/within-delta 8000000 2000000000)
(- nanosec-sync nanosec-async)))))
(testing "Actually computes the correct answers"
(is (match? [2 3 4] (applicative/eval-key env-with-sequence+delay :c))))
(is (match? [2 3 4] (applicative/eval-key env-with-sequence+delay :c {::applicative/context promesa/context}))))
(testing "resolve closure sequence"
(is (= [2 4 6]
(applicative/eval-key env-with-closure-sequence :y)))))
(applicative/eval-key env-with-closure-sequence :y {::applicative/context promesa/context})))))

(deftest schema-test
(let [env-with-schema {:a (>value 2)
Expand All @@ -192,17 +192,20 @@
:b (>value 1)
:c (yielding-schema (>leaf (+ ?a ?b)) s/Bool)}]
(testing "it should not fail"
(is (match? 3 (applicative/eval-key env-with-schema :c {::applicative/fvalidate schema/fvalidate}))))
(is (match? 3 (applicative/eval-key env-with-schema :c {::applicative/context promesa/context
::applicative/fvalidate schema/fvalidate}))))
(testing "returns ex-info when schema is selected as fvalidate, and schema fn validation is enabled"
(is (thrown-match? clojure.lang.ExceptionInfo
{:type :schema.core/error
:schema java.lang.Boolean
:value 3}
(ex-data
(s/with-fn-validation
(applicative/eval-key env-with-failing-schema :c {::applicative/fvalidate schema/fvalidate}))))))
(applicative/eval-key env-with-failing-schema :c {::applicative/context promesa/context
::applicative/fvalidate schema/fvalidate}))))))
(testing "doesn't validate when validation is disabled"
(is (match? 3 (applicative/eval-key env-with-failing-schema :c {::applicative/fvalidate schema/fvalidate}))))))
(is (match? 3 (applicative/eval-key env-with-failing-schema :c {::applicative/context promesa/context
::applicative/fvalidate schema/fvalidate}))))))

(deftest synchronous-applicative-test
(let [simple-env {:a (>value 2)
Expand Down

0 comments on commit ce2598f

Please sign in to comment.