On Thu, Jun 16, 2011 at 9:51 AM, Tassilo Horn <tass...@member.fsf.org> wrote:
> Hi all,
>
> I have some functions that use destructuring on a map parameter, and it
> seems I have a false assumption on the workings.  Take for example this
> one:
>
> (defn foo
>  [{:keys [a b]
>    :or {a 1 b 2}
>    :as all}]
>  [(hash-map :a a :b b) all])
>
> I expected it to always return a vector of two equal maps.  However,
> that's not true.  all is in fact the map I gave at the call, not the map
> with the default values declared in the :or applied.
>
> Why is that? So that you can distinguish given actual args that happen
> to match default values from real, non-given defaults?
>
> That somehow makes sense, but is there some way to get the complete map
> with defaults applied, too?

(defn foo
  [{:keys [a b]
    :or {a 1 b 2}
    :as all}]
  (let [all (merge {:a 1 :b 2} all)]
    [(hash-map :a a :b b) all]))

Of course this repeats the map of defaults, so this is ugly and will
get unwieldy if the default map gets much bigger. Some macro-fu can no
doubt help:

(defn keysm [m]
  (zipmap (map #(keyword (name %)) (keys m)) (vals m)))

(defmacro defnm [name argvec & body]
  `(defn ~name ~argvec
     (let ~(vec
             (apply concat
               (for [a argvec :when (and (map? a) (:or a) (:as a))]
                 [(:as a) `(merge ~(keysm (:or a)) ~(:as a))])))
       ~@body)))

(defnm bar
  [{:keys [a b]
    :or {a 1 b 2}
    :as all}]
  [(hash-map :a a :b b) all])

(defnm baz
  [{:keys [a b]
    :or {a 1 b 2}
    :as all} quux]
  [(hash-map :a a :b b) all quux])

(defnm quux
  [{:keys [a b]
    :or {a 1 b 2}
    :as all}
   {:keys [c d]
    :or {c 3}
    :as all2}]
  [(hash-map :a a :b b :c c :d d) all all2])

=> (foo {})
[{:a 1, :b 2} {}]
=> (bar {})
[{:a 1, :b 2} {:b 2, :a 1}]
=> (baz {} 3)
[{:a 1, :b 2} {:b 2, :a 1} 3]
=> (quux {} {})
[{:a 1, :c 3, :b 2, :d nil} {:b 2, :a 1} {:c 3}]
=> (quux {:b 3} {})
[{:a 1, :c 3, :b 3, :d nil} {:b 3, :a 1} {:c 3}]

Caveats:

1. As you can see, the sequence of keys can be rearranged between the
two maps. But you shouldn't be relying on key order anyway with
unsorted maps.

2. This bare-bones macro doesn't handle docstrings on the functions.

3. Map destructuring nested in some other destructuring form won't get
the special treatment; only at the top level.

4. You can't mix both behaviors of :as in the same function with
different destructured map arguments, other than by nesting the one
where you want the old :as behavior in some other destructuring form.

But it should cover the very common case of an undocumented (internal)
function with a single map parameter to destructure, and with a little
tweaking could be extended to handle docstrings properly.



>
> In my application, I don't care about if some value is an implicit or
> explicit default, but I have some functions with many keys that are
> split in two subfunctions.  One public frontend functions with :or in
> order to let callers see the defaults, and one private function that
> does the actual work, assumes correct args and is called by the public
> one:
>
>  (defn- foo-1 [{:keys [a b c]}] ...)
>
>  (def foo [{:keys [a b c]
>             :or {a 1 b 2 c 3}
>             :as all}]
>    ;; do some stuff
>    ;; (foo-1 all) ;; That won't work, instead I have to do the lengthy...
>    (foo-1 (hash-map :a a :b b :c c))
>    ;; do more stuff
>    )
>
> Any recourse?
>
> Bye,
> Tassilo
>
> --
> 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



-- 
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.

-- 
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

Reply via email to