metosin / sieppari

Small, fast, and complete interceptor library for Clojure/Script
Eclipse Public License 2.0
207 stars 21 forks source link

Map function into enter, not into handler #14

Open ikitommi opened 5 years ago

ikitommi commented 5 years ago

If functions would be mapped into interceptor enter, instead of special handler case, it would be easier to write one-way chains:

Instead of:

(def inc-x-interceptor
  {:enter #(update % :request inc)})

(defn handler [request]
  (inc (:x request)))

(s/execute
  [inc-x-interceptor handler]
  40)
;=> 42

one could say:

(def inc-x-interceptor
  #(update % :request inc)))

(def handler
  (s/handler
    #(inc (:x request)))

(s/execute
  [inc-x-interceptor handler]
  40)
;=> 42

... inlined:

(s/execute
  [#(update % :request inc))
   p/promise
   (s/handler #(inc (:x request))]
  40)
;=> 42

https://github.com/metosin/sieppari/blob/develop/src/sieppari/interceptor.cljc#L30-L35

jarppe commented 5 years ago

Interceptor enter differs from handler in that interceptor enter is called and is expected to returns ctx, where as the handler is called with request and expected to return response.

If we change IntoInterceptor for functions to returns interceptor with the function as enter, then we need some other way to handle handler.

Perhaps what ever is last in queue is assumed to be handler?

Or, we could make it so that every element in queue is an interceptor, and we just provide utility to convert handler into interceptor and require that the caller does the conversion. Something like:

(s/execute [some-interceptor other-interceptor (handler->interceptor my-handler)])

What do you think?

ikitommi commented 5 years ago

I think it's a good idea. And for ergonomics, s/handler would be better handler->interceptor. Also, like discussed in #15, the request-response could be a extra pattern on top of the core, which just runs contexts e2e.

nilern commented 5 years ago

It would be more symmetric and principled to have both enter-fn->interceptor and leave-fn->interceptor (or perhaps preprocessor and postprocessor) and having the handler be just a function. It would be more verbose but usually there aren't that many interceptors and the code is probably not modified as often as read.

Forcing the handler to be of interceptor type does not make much sense to me since it is a special case anyway because the context turns around and/or request is switched to response.

jjttjj commented 5 years ago

One thing I've been wondering: does the interceptor pattern inherently imply that the context should contain a :request key, "make a request" then return with a :response key? Are these specific keys part of the pattern? Or, if not the specific keys, the idea that something (besides the context) goes through a chain of functions, then something else comes back, and both these things are separated from the surrounding context?

Or is it more general, does the interceptor pattern only imply a context goes through the interceptors one way, then back another, and :request/:response just happen to often be useful?

I think answering this question would help answer how a function should be interpreted as an interceptor. If there is no separate concept of request/response, then clearly a function shouldn't work be a request->response handler.

I have been thinking about using the interceptor pattern. I currently have the request just as subset of the context itself. There is no real "standard" defining my request so I didn't initially see a need to seperate it from surrounding context. The context is built up from the chain of interceptors, then at the end I pick out what I need to make the request and return back up the chain. For what it's worth, I'm beginning to think maybe this is bad and I more clearly define my "request" and put it into a :request key.

nilern commented 5 years ago

The handler could also be a function ctx -> ctx... but really the handler should work like in prior art (Pedestal, re-frame). The request-response convention is nice, but not the only reasonable one (e.g. re-frame has coeffects and effects instead), so a general purpose library like this should not force its use.

ikitommi commented 5 years ago

There is a related issue of making the request & response optional: https://github.com/metosin/sieppari/issues/18

ikitommi commented 5 years ago

Also https://github.com/metosin/sieppari/issues/15