Here's the thing I can't stand about keyword args: Let's start off with a simple function that looks for keys x and y, and if either is missing, replaces the value with 1 or 2 respectively.
(defn f [& {:keys [x y] :or {x 1 y 2}}] [x y]) => (f :x 10) [10 2] So far, so good. Now, let's do an extremely simple test of composability. Let's define a function g that destructures the keyword args, and if a certain keyword :call-f is set, then we're just going to turn around and call f, passing all the keyword args along to f. (defn g [& {call-f :call-f :as m}] (when call-f (apply f m))) => (g :call-f true :x 10) [1 2] What? Oh right, you can't apply the function f to the map m. This doesn't work. If we want to "apply" f, we somehow need to apply it to a sequence of alternating keys and values, not a map. Take 2: (defn g [& {:keys [call-f x y] :as m}] (when call-f (f :x x :y y))) OK, so this time we try to workaround things by explicitly calling out the names of all the keywords we want to capture and pass along. It's ugly, and doesn't seem to scale well to situations where you have an unknown but at first glance, it seems to work: => (g :call-f true :x 80 :y 20) [80 20] Or does it? => (g :call-f true :x 10) [10 nil] What is going on here? Why is the answer coming out that :y is nil, when function f explicitly uses :or to have :y default to 2? The answer is that :or doesn't do what you think it does. The word "or" implies that it substitutes the default value of :y any time the destructured :y is nil or false. But that's not how it really works. It doesn't destructure and then test against nil; instead the :or map only kicks in when :y is actually missing as a key of the map. This means that in g, when we actively destructured :y, it got set to a nil, and then that got passed along to f. f's :or map didn't kick in because :y was set to nil, not absent. This is awful. You can't pass through keyword arguments to other functions without explicitly destructuring them, and if you destructure them and pass them along explicitly, nil values aren't picked up as absent values, so the :or default maps don't work properly. To put it simply, keyword args are bad news for composability. It's a shame, and I'd love to see this improved (rather than just having the community give up on keyword arguments). -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.