Fork me on GitHub
#architecture
<
2021-01-04
>
athomasoriginal17:01:45

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

lukas.rychtecky19:01:28

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

seancorfield17:01:41

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

athomasoriginal18:01:27

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.

clyfe18:01:43

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

seancorfield18:01:24

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
seancorfield17:01:13

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

seancorfield17:01:40

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
athomasoriginal17:01:08

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

jjttjj18:01:06

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

athomasoriginal18:01:57

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

seancorfield18:01:11

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

athomasoriginal18:01:07

Thanks for sharing! I had not seen ex before 🙇

athomasoriginal18:01:13

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

seancorfield18:01:04

Yes, standardized failure categories.

seancorfield18:01:14

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