architecture

athomasoriginal 2021-01-04T17:40:45.000700Z

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}]

seancorfield 2021-01-04T17:46:41.004300Z

"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.

codeasone 2021-01-12T10:54:48.017800Z

I've had a good experience with https://github.com/adambard/failjure for multi-stage processes where I wanted to accumulate progress/errors.

seancorfield 2021-01-04T17:49:13.006800Z

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

seancorfield 2021-01-04T17:52:40.008Z

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?

💯 2
👌 2
athomasoriginal 2021-01-04T17:58:08.008800Z

> “It Depends” 🙂 hehe my fav’ corfield response.

athomasoriginal 2021-01-04T18:01:27.010800Z

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.

2021-01-04T18:02:06.011600Z

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

clyfe 2021-01-04T18:04:43.011800Z

Or the Either monad from https://github.com/funcool/cats, and you get either a result or a bunch of errors.

clyfe 2021-01-04T18:05:43.012400Z

Or https://github.com/druids/rop.

athomasoriginal 2021-01-04T18:08:57.012900Z

Noted! interesting. Is this lib actively in use? It seems to only be specs. First time seeing this for me.

seancorfield 2021-01-04T18:09:24.013100Z

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.

➕ 1
seancorfield 2021-01-04T18:10:11.013300Z

We use it at work. The exoscale folks have a variant of it with more code support around it.

seancorfield 2021-01-04T18:10:30.013500Z

https://github.com/exoscale/ex/

athomasoriginal 2021-01-04T18:13:07.013800Z

Thanks for sharing! I had not seen ex before 🙇

athomasoriginal 2021-01-04T18:14:13.014200Z

So, the idea of anomalies and ex is to establish an error state convention, yes?

seancorfield 2021-01-04T18:33:04.014400Z

Yes, standardized failure categories.

seancorfield 2021-01-04T18:34:14.014600Z

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).

lukas.rychtecky 2021-01-04T19:38:28.015700Z

I use left/right from https://github.com/funcool/cats and functions like >>=