Hi Ditlev

I'm answering a question you're not quite asking, but have you tried
looking at http://clojure-liberator.github.io/liberator/? It is built
around the kind of flow control you're trying to apply to HTTP requests,
but is much richer and designed to follow RFC2616.

With respect to your questions:

> 1) decompose functions into smaller functions, regardless of these helper
functions being of no use anywhere else.
> 2) define a bunch of helper functions in a top-level let-expression -
again to avoid having functions which are too heavily indented?

Decomposing your functions into smaller function is good for more than just
code reuse. It is also helpful for testing smaller chunks of code,
isolating pure functions from impure functions, and abstracting out
implementation details. This means when you look at the central function,
you can read through the logic of the code, rather than the implementation
details being mixed in there.

I'd personally split the functions out into smaller functions to make
testing and grokkability easier, but putting them in a let binding isn't a
bad choice either.


On Wed, Dec 9, 2015 at 9:18 AM Ditlev Tøjner <toj...@gmail.com> wrote:

> In designing our (by now) two clojure-based REST services we are
> struggling with managing flow-control.
>
> (Disclaimer: earlier backgrounds are with Python, C, Javascript and once
> upon a time Java/C# etc)
>
> What we'd like is to accomplish a series of steps which we feel are
> universal to REST-services.
> In the context of PUT/POST that is:
> ---
> 1. ensure that the content is of a given format (application/json)
>
> 2. receive & validate input
> 2a. if the input is invalid, return error output (in our case describing
> what went wrong)
>
> 3. create a new entry / update an existing entry
> (Note if you know the entire URI, then using PUT for both
> updating/creating is acceptable)
>
> 4. format a response (typically containing the created/updated object)
> ---
>
> Fig1 details how, after several passes, we're dealing with this. The
> approach is heavily inspired
> by Ref1 and essentially executes each step outlined above with the
> understanding that failure might
> occur. If a failure occurs, an error message is returned (of course).
>
> We chose an approach similar to Ref1 because we wanted to abort on errors,
> occasionally recover
> (such as update becoming create if no record is found) and we wanted
> detailed errors which
> precludes the use of (and ...).
>
> The astute observer may note that our model fails to capture the concept
> of recoverable failures
> - update-record* may fail for more reasons than simply not having a row
> (transient errors, actual
> logic-related errors etc) some of which are non-recoverable.
>
> As it stands, this means the function needs refactoring, *again*.
> What all this boils down to is this: given the unique interplay of
> immutability with LISP-style
> (AST) syntax - which techniques are successfully employed to avoid heavily
> indented code ? Is
> the only solution really to either:
>
> 1) decompose functions into smaller functions, regardless of
> these helper functions being of no use anywhere else.
>
> 2) define a bunch of helper functions in a top-level let-expression -
> again to avoid having
>    functions which are too heavily indented?
>
>
> Does it ever get easier ? Are we missing something ? It seems extreme to
> reach for monads for
> something of this nature.
>
> References
> 1. https://brehaut.net/blog/2011/error_monads (Ref1)
>
> Fig. 1. - a PUT endpoint
> ------------------------
> (defn update*
>   "create/update a pin."
>   [cid source rq]
>   (err/attempt-all
>    [_ (chk/mediatype-in? (get rq :content-type) ["application/json"])
>     rq (clojure.walk/keywordize-keys rq)
>     ;; data which can come from the user
>     input (merge {:method "app"
>                   :seen_at (-> (at/now-utc) (at/->dt-utc-str))}
>                  (get rq :body)
>                  {:source_type (get source :type) :source_id (get source :
> id)})
>     _ (av/input-valid? validate-pin-params input)
>
>
>     ;; additional data extracted from URI & token
>     record-data (merge input
>                        {:cid cid
>                         :uid (rq->uid* rq)})
>     record (err/any (update-record* record-data)
>                     (new-record* record-data))]
>    ;; Success
>    (rsp/created
>     (url cid source)
>     {:key-name "pinid"
>      :body {:request (model/transform record)}})
>    ;; Failure
>    err/handle-failure))
>
> ------------------------
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>
-- 
Daniel

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to