Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure-coding standards-for invoking functions with many arguments

Tags:

clojure

I am currenly using a function

(def mymap {})

(defn function1 [var1 var2 var3 var4 var5]
  ;calls another functions with all variables.
  (function2 var1 var2 var3 var4 var5)
)

But as this is having more parameters I would like to convert this to a map before calling functions2.

(function2((assoc mymap (keyword var1) var1
               (keyword var2) var2
               (keyword var3) var3
               (keyword var4) var4
               (keyword var5) var5 ))
  )

Is this the correct way? Do we have better way to do this(In java we direclty use some objects in this scenario)

like image 774
TomJava Avatar asked Apr 22 '26 23:04

TomJava


1 Answers

For general function args, I always go in order from the biggest to the smallest, in either size or "importance" (somewhat subjective).


However, if you have more than 3 args, I prefer to pass a map containing the args and appropriate keyword names.

The Tupelo Clojure library has some tools to make this easy. The macro vals->map takes multiple variable names and constructs a map from the (keywordized) variable name to its value, like:

  (let [ctx (let [a 1
                  b 2
                  c 3
                  d 4
                  e 5]
              (t/vals->map a b c d e))]
    (is= ctx {:a 1 :b 2 :c 3 :d 4 :e 5})

The macro with-map-vals does the opposite, deconstructing map values into local variables named for their keys. It is similar to the Clojure :keys destructuring, but in (IMHO) a more natural form:

    (let [{:keys [a b c d e]} ctx]    ; Clojure destructing syntax
      (is= [a b c d e] [1 2 3 4 5]))

    (t/with-map-vals ctx [a b c d e]
      (is= [a b c d e] [1 2 3 4 5])
      (is= 15 (+ a b c d e)))

    (t/with-map-vals ctx [b a d c e]  ; order doesn't matter
      (is= [a b c d e] [1 2 3 4 5])
      (is= 15 (+ a b c d e)))

    (t/with-map-vals ctx [b a d]      ; can ignore stuff you don't care about
      (is= [d a b] [4 1 2]))

    (throws?
      (t/with-map-vals ctx [a b z]    ; throws if key doesn't exist
        (println "won't ever get here")))))

If you have nested data in maps and/or vectors, you can use the more powerful destruct and restruct tools. Here is a brief example (from the unit tests):

  (let [info  {:a 1
               :b {:c 3
                   :d 4}}
        mania {:x 6
               :y {:w 333
                   :z 666}}]

    (t/it-> (t/destruct [info {:a ?
                               :b {:c ?
                                   :d ?}}
                         mania {:y {:z ?}}] ; can ignore unwanted keys like :x
              (let [a (+ 100 a)
                    c (+ 100 c)
                    d z
                    z 777]
                (restruct-all)))
      (t/with-map-vals it [info mania]
        (is= info {:a 101, :b {:c 103, :d 666}})
        (is= mania {:x 6, :y {:w 333, :z 777}})))

As you can see, a question mark ? will cause the corresponding value to be destructed into a variable named for the corresponding keyword. It is also possible to create explicit variable names like so:

(dotest
  (let [info  {:a 777
               :b [2 3 4]}
        mania [{:a 11} {:b 22} {:c [7 8 9]}]]
    (let [z ::dummy]
      (t/it-> (t/destruct [info {:a z
                                 :b [d e f]}
                           mania [{:a ?} BBB {:c clutter}]]
                (let [clutter (mapv inc clutter)
                      BBB     {:BBB 33}
                      z       77
                      d       (+ 7 d)]
                  (restruct-all)))
        (t/with-map-vals it [info mania]
          (is= info {:a 77, :b [9 3 4]})
          (is= mania [{:a 11} {:BBB 33} {:c [8 9 10]}]))))))

It also works for mixed maps & vectors:

  (let [data {:a [{:b 2}
                  {:c 3}
                  [7 8 9]]} ]
    (t/destruct [data {:a [{:b p}
                           {:c q}
                           [r s t]]} ]
      (is= [2 3 7 8 9] [p q r s t])))
like image 88
Alan Thompson Avatar answered Apr 26 '26 15:04

Alan Thompson



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!