Should I use vector to return 2 or 3 pieces of information from a function?

Hi all,

Could you please let me know what’s the usual structure when I want a function to return a small number of items?

I’m thinking that the simplest method is to return a vector and then get values using destructuring.

(defn find-product-price []
 ;; some code
  (let [price 111
        currency "USD"
        discounted false]
    [price currency discounted]))

(defn get-message-for-customer []
  (let [[price currency discounted] (find-product-price)]
    (if discounted
      (str "Discount " price " " currency)
      (str "The customer will pay " price " " currency))))

(get-message-for-customer)
;; => "The customer will pay 111 USD"

But somewhere (I don’t remember where) I read that getting values from a vector by indexing it is not the proper way. So should I rather use Map? If not how many elements are still OK and how many should rather be returned as a Map?

Thank you.

Best regards,

Paul

1 Like

This is okay for just two or maybe three “results” but you have to remember the order. It’s more common to return a hash map where your results all have names (and can still be destructured easily):

(defn find-product-price []
  ;; some code
  (let [price 111
        currency "USD"
        discounted false]
    {:price price :currency currency :discounted discounted}))

(defn get-message-for-customer [] 
  (let [{:keys [price currency discounted]} (find-product-price)]
    (if discounted
      (str "Discount " price " " currency)
      (str "The customer will page " price " " currency))))

Returning the named items is slightly more verbose but the destructuring is straightforward – and you can easily have default values now (via :or in the destructuring) – and the rest of the code doesn’t change.

If you really don’t like the verboseness of building a hash map from local bindings, there are any number of utility libraries out there that let you do something like this:

(defn find-product-price []
  ;; some code
  (let [price 111
        currency "USD"
        discounted false]
    (local-map :only :price :currency :discounted)))
    ;; or just (local-map) if you want all local symbols in it

See, for example: worldsingles/commons: Common utility functions and “extensions” to Clojure (github.com)

4 Likes

I don’t consider it verbose at all. And I actually like it over the proposed vector since I’ll be changing stuff a lot when building my first projects in Clojure and it’s much easier to add a key since adding key doesn’t break API. So yeah, I’m going to use map even for 2 pieces of information.

Thank you @seancorfield , super helpful.

I’ll definitely check this out ;-). Thx!

1 Like