Unless I'm missing something subtle, all of your points would hold if you removed the & in your argument vector to turn your kwargs into an explicit map, wouldn't they? One advantage is you'd be able to (apply f [m]), but I'm not sure the :or logic would be any less troublesome.
On Wed, Apr 30, 2014 at 8:06 PM, Mark Engelberg <mark.engelb...@gmail.com>wrote: > 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. > -- 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.