What are people’s preference for “Business Logic” functions return values. For example:
(get-products)
=> [{,,,prod} {,,,prod} {,,,prod}]
(get-products)
=> [true [{,,,prod} {,,,prod} {,,,prod}]]
=> [false {,,,error}]
(get-products)
=> {:status true :results [{,,,prod} {,,,prod} {,,,prod}]]
=> {:status false :results {,,,error}]
"It Depends" 🙂 Under various different circumstances I might have a function return nil
to mean I failed (and thus []
to mean I succeeded but found no products). If the scope of use is very small, I might use the second approach
(let [[success products] (get-products)]
(if success
(process products)
(some-other-path)))
but if the scope is larger (e.g., get-products
is part of a more widely used API) then I'll use the hash map approach since it is more readable.I've had a good experience with https://github.com/adambard/failjure for multi-stage processes where I wanted to accumulate progress/errors.
Often, when I return a hash map like that, I will also return :errors
for the failure case (and sometimes I'll omit the Boolean since you can just do:
(let [products (get-products)]
(if-let [errors (:errors products)]
(handle-those errors)
(process (:results products))))
If you go with the hash map approach -- be consistent and use the same keys everywhere you do it. Write a Spec for it. Maybe even write predicates for succeeded?
/ failed?
> “It Depends” 🙂 hehe my fav’ corfield response.
Are there ever scenarios where your using all three and have you found that can introduce its own challenges? By challenge, i’m thinking just difficult achieving a flow state because one might not know the possible return type.
Probably worth noting https://github.com/cognitect-labs/anomalies for their approach of marking an error (anomalous?) result with a particular keyword existing in a map
Or the Either monad from https://github.com/funcool/cats, and you get either a result or a bunch of errors.
Noted! interesting. Is this lib actively in use? It seems to only be specs. First time seeing this for me.
If you're composing functions that consistently return results (success) or errors (failure) then, yes, the either monad or something similar is probably worthwhile. But having to reach for monads would make me consider the flow of that code: I'd want to see if I could simplify the pipeline of operations at that point first.
We use it at work. The exoscale folks have a variant of it with more code support around it.
Thanks for sharing! I had not seen ex
before 🙇
So, the idea of anomalies
and ex
is to establish an error state convention, yes?
Yes, standardized failure categories.
It's a bit biased toward HTTP status style failures but they can be used more broadly (in particular: note whether the failure indicates caller or callee and whether a caller could expect success on the same call later on).
I use left/right from https://github.com/funcool/cats and functions like >>=