Reputation: 11130
The source for faraday's scan function (https://github.com/ptaoussanis/faraday/blob/master/src/taoensso/faraday.clj#L1197) has a destructuring form that I am struggling to understand...
(source far/scan)
(defn scan
"..."
[client-opts table
& [{:keys [attr-conds last-prim-kvs span-reqs return limit total-segments
filter-expr
segment return-cc?] :as opts
:or {span-reqs {:max 5}}}]]
...)
What does that :or {span-reqs {:max 5}}
do?
Upvotes: 6
Views: 2748
Reputation: 9266
:or {span-reqs {:max 5}}
specifies that iff opts
does not have the key :span-reqs
, span-reqs
will be bound to the map {:max 5}
.
Note that span-reqs
does not directly refer to the key :span-reqs
- this would also be possible:
(defn scan
[client-opts table
& [{:keys [attr-conds last-prim-kvs return limit total-segments
filter-expr
segment return-cc?] :as opts
foo :span-reqs
:or {foo {:max 5}}}]])
Observe that in the :or
map you can generally provide default expressions for symbols you have bound in the destructoring form.
N. B.: Be careful with putting runtime expressions in :or
. As of Clojure 1.9-alpha14 they will still be evaluated regardless of whether they are needed (see http://dev.clojure.org/jira/browse/CLJ-1676).
Upvotes: 6
Reputation: 91554
To put this in context I'll wrap it in an example function:
user> (defn foo [client-opts
table
& [{:keys [attr-conds
last-prim-kvs
span-reqs
return
limit
total-segments
filter-expr
segment return-cc?]
:as opts
:or {span-reqs {:max 5}}}]]
(println "client-opts")
(clojure.pprint/pprint client-opts)
(println "table")
(clojure.pprint/pprint table)
(println "opts")
(clojure.pprint/pprint opts)
(println "the symbol attr-conds is bound to:" attr-conds)
(println "the symbol limit is bound to:" limit)
(println "the symbol span-reqs is bound to:" span-reqs))
#'user/foo
user> (foo :I'm-a-client-opt
:I'm-a-table
{:attr-conds 1
:span-reqs [1 2 3]
:limit 47}
{:attr-conds :i-will-be-ignored}
{:limit :i-will-also-be-ignored})
client-opts
:I'm-a-client-opt
table
:I'm-a-table
opts
{:attr-conds 1, :span-reqs [1 2 3], :limit 47}
the symbol attr-conds is bound to: 1
the symbol limit is bound to: 47
the symbol span-reqs is bound to: [1 2 3]
nil
Now we see that it binds some names to parts of the first map in a list, so lets take that destructing expresion apart:
& ;; this symbol collects all the rest of the arguments into a list
[ ;; this one then does list destructuring on the list indicated by the &
{:keys [attr-conds ;; This block destructures the first (and only the first) entry in the vector.
last-prim-kvs ;; Because this is a map it will do map destructuring and
span-reqs ;; bind (assigns) values to symbols in this list
return
limit
total-segments
filter-expr
segment return-cc?]
:as opts ;; the entire map in the first position in the list created by the & will be bound to the name opts
:or {span-reqs {:max 5}}}] ;; if span-reqs is not in the map at all,
;; then the map {:max 5} will be bound to the name
;; span-reqs instead
So the keyword :or in that nested destructuring expression assigns a default value to a symbol, if the third argument to the function is a map that does not provide a value for the keyword :span-reqs. It does nothing if the keyword :span-reqs is present and contains other keys without providing a value for :max. It does not merge the default values into the map in this context, it just provides a value if it's completely missing:
here is is with the value not specified at all:
user> (foo :I'm-a-client-opt
:I'm-a-table
{:attr-conds 1
;; :span-reqs [1 2 3] ;; removed this one
:limit 47}
{:attr-conds :i-will-be-ignored}
{:limit :i-will-also-be-ignored})
client-opts
:I'm-a-client-opt
table
:I'm-a-table
opts
{:attr-conds 1, :limit 47}
the symbol attr-conds is bound to: 1
the symbol limit is bound to: 47
the symbol span-reqs is bound to: {:max 5}
nil
and again with a value specified, where that value does not include a :max
user> (foo :I'm-a-client-opt
:I'm-a-table
{:attr-conds 1
:span-reqs {:min -7} ;; present but contains something else
:limit 47}
{:attr-conds :i-will-be-ignored}
{:limit :i-will-also-be-ignored})
client-opts
:I'm-a-client-opt
table
:I'm-a-table
opts
{:attr-conds 1, :span-reqs {:min -7}, :limit 47}
the symbol attr-conds is bound to: 1
the symbol limit is bound to: 47
the symbol span-reqs is bound to: {:min -7}
nil
Upvotes: 2
Reputation: 29958
It is the default value. For more details please see http://clojure.org/guides/destructuring
(def my-map {:a "A" :b "B" :c 3 :d 4})
(let [{a :a, x :x, :or {x "Not found!"}, :as all} my-map]
(println "I got" a "from" all)
(println "Where is x?" x))
;= I got A from {:a "A" :b "B" :c 3 :d 4}
;= Where is x? Not found!
using :keys
we get
(let [{:keys [a x] :or {x "Not found!"}, :as all} my-map]
(println "I got" a "from" all)
(println "Where is x?" x))
with the same result
Upvotes: 10