(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}))