Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting default values in clojure defn

I'm super new to clojure and I am trying to set fault values for args in a clojure function. This is what I have so far:

(defn get-user-projects-by-user-id
([db-conn userid] (get-user-projects-by-user-id db-conn userid (str "asc") nil 10 10))
([db-conn userid sort filters offset size]
      ;;rest of the function

What I am trying to accomplish is whenever the function doesn't receive sort, filters, offset, or size, or as nil/false, they get the default values of "asc", nil, 10, and 10, respectively.

However, whenever I don't send any of those arguments to get-user-projects-by-user-id, (println sort filters offset size) is nil nil nil nil.

How can I set them to the default valuses I want?

like image 584
Kaisin Li Avatar asked Feb 06 '26 23:02

Kaisin Li


2 Answers

Here there are 3 ways I can think of:

Your version:

(defn get-user-projects-by-user-id
  ([db-conn userid] (get-user-projects-by-user-id db-conn userid "asc" nil 10 10))
  ([db-conn userid sort filters offset size]
   (println "arguments: sort:" sort "filters:" filters "offset:" offset "size:" size)))

;; (get-user-projects-by-user-id 'db 'userid)
;; => arguments: sort asc , filters nil , offset 10 , size 10

Another style would be to pass an opts parameter which is a map with the options you want, and you can provide default values in a convenient manner with destructuring:

(defn get-user-projects-by-user-id-with-opts
  ([db-conn userid]
   (get-user-projects-by-user-id-with-opts db-conn userid nil))
  ([db-conn userid {:keys [sort filters offset size]
                    :or   {sort "asc" offset 10 size 10}}]
   (println "arguments: sort:" sort "filters:" filters "offset:" offset "size:" size)))

;; (get-user-projects-by-user-id-with-opts 'db 'userid)
;; => arguments: sort: asc filters: nil offset: 10 size: 10

;; (get-user-projects-by-user-id-with-opts 'db 'userid {:sort "desc" :offset 20})
;; => arguments: sort: desc filters: nil offset: 20 size: 10

Finally, there's a third approach which was used sometimes in older libraries, which was to pass an arbitrary number of arguments to your function, and take the optional arguments after the others (this is a bit like Python I guess):

(defn get-user-projects-by-user-id-with-varargs
  [db-conn userid & args]
  (let [arg-map (apply hash-map args)
        {:keys [sort filters offset size]
         :or   {sort "asc" offset 10 size 10}} arg-map]
    (println "arguments: sort:" sort "filters:" filters "offset:" offset "size:" size)))

;; (get-user-projects-by-user-id-with-varargs 'db 'userid)
;; => arguments: sort: asc filters: nil offset: 10 size: 10

;; (get-user-projects-by-user-id-with-varargs 'db 'userid :sort "desc" :offset 20)
;; => arguments: sort: desc filters: nil offset: 20 size: 10
like image 122
Denis Fuenzalida Avatar answered Feb 12 '26 05:02

Denis Fuenzalida


There are several ways for providing optional arguments and their default values. Which to choose depends on how you want to provide those optional arguments to the function call.

Strictly Serial Arguments

Your arguments are already ordered by decreasing likelihood for setting them. You wish to be able to call the function like this (assuming a println function body and ignoring the nil return value):

user> (get-user-projects-by-user-id :conn :uid)
[:conn :uid asc nil 10 10]
user> (get-user-projects-by-user-id :conn :uid :sort)
[:conn :uid :sort nil 10 10]
user> (get-user-projects-by-user-id :conn :uid :sort "filter")
[:conn :uid :sort filter 10 10]
user> (get-user-projects-by-user-id :conn :uid :sort "filter" 9)
[:conn :uid :sort filter 9 10]

For this, you can overload the function by arity as you already started:

(defn get-user-projects-by-user-id
  ([db-conn userid]
   (get-user-projects-by-user-id db-conn userid
                                 (str "asc")
                                 nil
                                 10
                                 10))
  ([db-conn userid sort]
   (get-user-projects-by-user-id db-conn userid
                                 sort
                                 nil
                                 10
                                 10))
  ([db-conn userid sort filters]
   (get-user-projects-by-user-id db-conn userid
                                 sort
                                 filters
                                 10
                                 10))
  ([db-conn userid sort filters offset]
   (get-user-projects-by-user-id db-conn userid
                                 sort
                                 filters
                                 offset
                                 10))
  ([db-conn userid sort filters offset size]
   (println [db-conn userid sort filters offset size])))

It's a bit tedious in the function definition and you must take care when refactoring to keep the default values correct.

Optional Arguments as a Map

You can use destructuring in your argument vector to allow passing a map with extra arguments. This allows passing them in any order: you can override offset without having to pass sort and others, too:

(defn get-user-projects-by-user-id-extra-map
  [db-conn userid & [{:keys [sort filters offset size]
                      :or   {sort    "asc"
                             filters nil
                             offset  10
                             size    10}}]]
  (println [db-conn userid sort filters offset size]))

You use it like this:

user> (get-user-projects-by-user-id-extra-map :conn :uid {:offset 9})
[:conn :uid asc nil 9 10]

Optional Arguments as Pairs

If you change the destructuring slightly (note the missing []), it allows you to pass the optional arguments as key-value-pairs without the need for a map. This is usually easier to use when your function calls are all explicit whereas the previous option is often easier when you apply your function with programmatically collected extra arguments.

(defn get-user-projects-by-user-id-pairs
  [db-conn userid & {:keys [sort filters offset size]
                     :or   {sort    "asc"
                            filters nil
                            offset  10
                            size    10}}]
   (println [db-conn userid sort filters offset size]))

Use it (note missing {}):

user> (get-user-projects-by-user-id-pairs :conn :uid :offset 9)
[:conn :uid asc nil 9 10]
like image 32
Stefan Kamphausen Avatar answered Feb 12 '26 05:02

Stefan Kamphausen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!