; on Clojure 1.11.0-alpha1
(defn example2
[& {:keys [first
second
third
fourth]
:or {first 11
second 22
third 33
fourth 44}
:as opts}]
(str "example1: " first second third fourth))
(defn example1
[& {:keys [first
second
third
fourth]
:or {first 11
second 22
third 33
fourth 44}
:as opts}]
(str "example1: " first second third fourth
", "
(example2 opts)))
(defn main-function
[& {:keys [first
second
third
fourth]
:or {first 11
second 22
third 33
fourth 44}
:as opts}]
(str "main-function: " first second third fourth
", "
(example1 opts)))
(main-function)
; "main-function: 11223344, example1: 11223344, example1: 11223344"
(main-function :first "aa")
; "main-function: aa223344, example1: aa223344, example1: aa223344"
And as you can see, the defaults in or: are always the same. So it makes sense to do something like this:
From the error I understand that the map keys should be symbols. I’m assuming default opts is a common practice so I’d like to ask experienced people, how are you handling this?
Thank you bsless. The problem is that you have to then remember to pass a lot of default opts when you call those subsequent functions directly outside of the main function since they wouldn’t have any defaults in :or specified…
The keys in default-opts are being turned into namespace qualified symbols because of the syntax quote (`) ie if you are in the user namespace, the keys are the symbols (not keywords) user/first, user/second instead of just :first, :second etc.
Thank you @xfthhxk , based on my understanding 'symbol should be the correct way to get symbols that are not namespace qualified. But that doesn’t work either:
So having a reference for the :or is not something I thought of doing before. I get the same error as you do. Tried to isolate this a bit more by trying the following.
(def default-opts
{'first 11
'second 22
'third 33
'fourth 44})
(let [{:syms [first
second
third
fourth]
:or default-opts} {'first 12}])
Note I used {:syms [,,]} instead of {:keys [,,]} since default-opts has symbols.
It appears that the value for the :or key in the map destructuring must be a map literal based on the spec error. My guess is that the error is probably thrown at macro expansion time, at which time default-opts is a symbol and hence the error.
Macros expand outside in, unlike functions which evaluate inside out, that means that defn runs before your macro, that’s what allows them to take over the evaluation order and rules.
The downside is that they don’t compose unless they are designed together with some form of composition in mind.
If you wanted the behavior you described, what you’d need to do is write a macro that replaces defn or wraps defn itself.
You’re probably better here to just use merge inside a let like someone else said before:
(def defaults
{:first 1
:second 2
:third 3
:fourth 4})
(defn example1
[m]
(let [{:keys [first second third fourth]} (merge defaults m)]
...))
(defn example2
[m]
(let [{:keys [first second third fourth]} (merge defaults m)]
...))
(defn main
[m]
(let [{:keys [first second third fourth]} (merge defaults m)]
...))
In my opinion easy to read and without all that merging it might actually performs better. I still would prefer to just insert all default-opts but this seems good enough.