Perfect, thanks Stuart! That works great :)
I put it in a convenience macro:
; Stuart Sierra
(defmacro defn-with-doc
  "Like defn but accepts a procedurally generated string."
  [fun doc-str & body]
  `(let [f# (defn ~fun ~...@body)]
     (alter-meta! (var ~fun) assoc :doc ~doc-str)
     f#))

Got me thinking on a different line....
Laurent once remarked that it would be nice to attach argument
descriptions as meta data:

(defmacro defn-with-args
  "Like defn but takes non-literal string doc,
  and a map of arguments instead of a vector.
  args should be a map of argument names to descriptions."
  [fun doc-str args & body]
  `(let [f# (defn ~fun ~(vec (map symbol (keys args)))
              ~...@body)]
     (alter-meta! (var ~fun) assoc :doc
                  ~(str doc-str \newline "  "
                       args))
     (alter-meta! (var ~fun) assoc :argdescs
                  ~(vec (vals args)))
     f#))

  (let [m {:one 1, :two 2}]
    (defn-with-args foo1
      "hi1"
      {"in1" "must be a string", "in2" (str "must be one of: " (keys
m))}
      (println in1 (m in2))
      (m in2)))

-------------------------
test-extensions/foo1
([in1 in2])
  hi1
  {"in1" "must be a string", "in2" (str "must be one of: " (keys m))}

(println (:argdescs ^(var foo1)))
[must be a string must be one of: (:one :two)]

My code is almost but not quite, but you get the idea... I think this
might be quite useful with a bit of improvement. I imagine Laurent is
mostly interested in the arguments from an IDE perspective, but in
general its nice to know precisely what arguments are expected.


Also a few people have discussed the "by contract" style. Another
possible extension would be to store preconditions:
(def *debug* false)
(defmacro check-preconditions
  "Ensure preconditions are met."
  [m]
  (if *debug*
    `(assert (apply = true (map #((val %) (key %)) ~m)))))
(defmacro defn-by-contract
  "Like defn but takes non-literal string doc,
  and a map of arguments instead of a vector.
  args should be a map of strings to preconditions."
  [fun doc-str args & body]
  `(let [f# (defn ~fun ~(vec (map symbol (keys args)))
              (check-preconditions args)
              ~...@body)]
     (alter-meta! (var ~fun) assoc :doc
                  ~(str doc-str \newline "  "
                       args))
     (alter-meta! (var ~fun) assoc :argdescs
                  ~(vec (vals args)))
     f#))

Actually preconditions should be handled completely separately perhaps
as another metadata :argpreds, this code is just to illustrate a
possibility.

If anyone is interested in playing with this, feel free to use:
http://github.com/timothypratley/strive/blob/baf83e2bb26662f5f5049d165dec31e47b91e171/clj/timothypratley/extensions.clj
http://github.com/timothypratley/strive/blob/baf83e2bb26662f5f5049d165dec31e47b91e171/clj/timothypratley/test-extensions.clj



Regards,
Tim.

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