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

Remove the default engine behavior in internal applicative eval functions #41

Merged
merged 4 commits into from
Nov 18, 2024
Merged
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
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
Loading