I’m curious if people here generally feel comfortable passing a function’s whole map of named args to another function (called within the original) that may only need a subset of the named args.
Context: I often find I am calling another function with a subset of named args from the original function. I’ve gone back and forth on whether to just pass the second function the original function’s whole original arg map or to pull out the subset of args for the second function explicitly, typically by using select-keys
.
An example might be clarifying:
;;This function needs a subset of keys from the one below
(defn auth-token
[{:keys [user-id authf]}]
(auth-provider/auth (authf (user user-id))))
;;This function calls the above with a subset of its keys
(defn foo
[{:as args :keys [item-id price currency user-id authf]}]
;;... do some things
;;This (concise):
(auth-token args)
;;Or this (better to be more explicit?):
(auth-token (select-keys args [:user-id :authf])))
For a long time I would use the first instance above, just passing the args map from one function to the next. At a certain point, though, I found I was losing track of what I was passing and sometimes passing args that caused problems down the line. For example if you have function a
calling b
calling c
(a -> b -> c
), it might work fine to pass the whole arg map from one to another until either 1> a
changes to accept new arguments that cause c
to behave in a new/unexpected way, 2> c
changes to accept new arguments that cause the args map from a
or b
to be interpreted in an unexpected way.
On the other hand, today I was going through some code that uses the select-keys
style and it seemed perhaps unecessarily verbose (particularly when you have (a -> b
) and b
is not calling anything else with named args). It occurred to me that if I clearly document the arguments for each function, and review this when deciding how to pass args, this will help avoid any issues with the more concise style.