Skip to content

Instantly share code, notes, and snippets.

@jmayaalv
Last active March 2, 2019 04:02
Show Gist options
  • Save jmayaalv/827b63c313947dd7fd557bd16beefc4e to your computer and use it in GitHub Desktop.
Save jmayaalv/827b63c313947dd7fd557bd16beefc4e to your computer and use it in GitHub Desktop.
(def cofx-now
"injects current time to context"
{:enter (fn [ctx]
(assoc-in ctx [:request :now] (java.util.Date.)))})
;; interceptor, in enter update value in `[:request :x]` with `inc`
(def inc-x-interceptor
{:enter (fn [ctx]
(update-in ctx [:request :x] inc))})
;; this is the business logic. There could be a macro that would generate this. something like
;; (defhandler somebusines-logic {:interceptors [inc-x-interceptor cofx-now]}
;; {:y (inc (:x request))
;; :now (:now request)}))
(def some-business-logic (with-meta
(fn [request]
{:y (inc (:x request))
:now (:now request)})
{:interceptors [inc-x-interceptor cofx-now]}))
;; This is internal code, programmer wouldn't see. But what it does is to extract the interceptors from the metadata and inject as cofx
(defn execute-handler [handler args]
(let [interceptors (into [] (:interceptors (meta handler)))]
(sieppari/execute (conj interceptors handler) args)))
(comment
(execute-handler some-business-logic {:x 40})
)
(defonce co-effects (atom {}))
(defn register-cofx [name f]
(swap! co-effects assoc name f))
(register-cofx :now (fn [ctx]
(assoc-in ctx [:request :now] (java.util.Date.))))
(defn defn-from [name mdata args cofx & body]
`(defn ~(symbol name) ~mdata ~args
(sieppari.core/execute (conj ~cofx (fn ~args ~@body))
~@args)))
(defmacro defhandler
"Simplifies the creation of functions that declare coeffects.
```
Example:
(defhandler some-business-logic [request]
[:now]
{:time (:now request)})
```
"
[name _ cofxs & body]
(defn-from name {:coeffects cofxs} '[request] (mapv #(hash-map :enter (get @co-effects %)) cofxs)
(cons 'do body)))
(defhandler test2 [request]
[:now]
(println request)
{:changed true
:db (:now request)})
(comment
(test2 {:now :ok}))
@jmayaalv
Copy link
Author

jmayaalv commented Mar 1, 2019

the point is that some-business-logic declares what it needs from outside, and the data gets injected "magically" this makes the function pure and makes business logic easy to test.
In this case, the "business-logic" is saying it needs the current time and the vallue of x increased by one, but there could be coeffects like queries, rest calls, etc.

Next thing would be to create the "effects" so that business logic returns what needs to be done instead of doing it, like in re-frame

{:db (->User {...})
:email {:to xx :body:"some email"}
:event {:name :event/launched :time now}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment