I am trying to write a macro that takes in a function and returns its args lists. This is what I have:
(defmacro print-arglists
[f]
`(:arglists (meta (var '~f))))
This works when I call it from the REPL or pass in a function directly:
(print-arglists map)
;; => ([f] [f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
But I want to be able to call it from another function like this:
(defn foo
[f]
...
(print-arglists f))
But that does not compile. I get the following error:
Unable to resolve var: f in this context
I am not sure what I am missing. Any advice/help would be appreciated.macros only receive the syntax/code (ie. f
in this case). Getting the arglists from a function is kind of tricky and I'm not sure there's any guaranteed way to do that. This is probably the closest, but relies on some clojure implementation details that you probably shouldn't rely on:
;; from <https://github.com/clojure/spec.alpha/blob/8800fdbeb267515774ef405452805456e248ce9f/src/main/clojure/clojure/spec/alpha.clj#L131>
(defn- fn-sym [^Object f]
(let [[_ f-ns f-n] (re-matches #"(.*)\$(.*?)(__[0-9]+)?" (.. f getClass getName))]
;; check for anonymous function
(when (not= "fn" f-n)
(symbol (clojure.lang.Compiler/demunge f-ns) (clojure.lang.Compiler/demunge f-n)))))
(defn fn-arglists [f]
(let [sym (fn-sym f)
v (resolve sym)]
(-> v
meta
:arglists)))
> (fn-arglists +)
;; ([] [x] [x y] [x y & more])
> macros only receive the syntax/code (ie. `f` in this case). Ah! I did not realize that. Thank you for pointing that out.
I do have another, not guaranteed, way to get arglists based on reflection. Your function, however, is very interesting.
Your response has taught me a lot. Thank you!
How does the reflection technique work? That sounds interesting.
Give me a minute to dig up the code.
(defn- non-defn->arities
[f]
(let [arity-info (->> f
class
.getDeclaredMethods
(map (fn [x] method->name-and-arity x))
(apply merge-with into)
(methods->arities f))]
arity-info))
(defn- methods->arities
[m arities]
(let [arity-list (sort < (:invokeStatic
arities (:invoke
arities (:doInvoke
arities []))))
is-variadic (instance? clojure.lang.RestFn m)]
(if is-variadic
(conj (map (fn [x] {:arg-count x :variadic false}) (butlast arity-list))
{:argcount (last arity-list) :variadic true})
(map (fn [x] {:arg-count x :variadic false}) arity-list))))
(defn- method->name-and-arity
[m]
{(keyword (.getName m)) [(method->arity m)]})
(defn- method->arity
[m]
(->> m .getParameterTypes alength))
It's a little bit of a mess, but that is the gist of the code.
It was inspired by the discussion here: https://stackoverflow.com/questions/1696693/clojure-how-to-find-out-the-arity-of-function-at-runtime
cool!
Hey guys, Anyone have an easy way to test multipart/form-data requests with ring-mock?
Hi @kimo - was just reading through some old slacks and didn't see if you got an answer.
I have been running into the same thing, and solved it using the peridot
library.
(peridot.multipart/build {"name" value-or-file})
Out of the box, the library supports multipart requests to send java.io.File
s and it converts other types to strings before sending. If you need support for sending other data types (such as sending InputStreams
), you can extend their multimethod, peridot.multipart/add-part
.
what is the best way to parse dates from CSV in Clojure as it stands?
@cstmlcodes CSV is just text, so no different than parsing dates from any other string
I am looking for a way to rate-limit a contact form on a webpage and came up with this solution after some toying around. As an example, if you called (is-rate-limited {:limit 5 :duration-in-secondes 10})
, the first 5 calls in 10 seconds would return true
, the 6th would return false
. Is there a more idiomatic way to do this, or a way without an atom?
There are Clojure libraries available that implement rate-limits/circuit breakers.
https://github.com/josephwilk/circuit-breaker https://github.com/brunoV/throttler (I haven’t used them, but probably worth a look)
The page is too small to make me want to add a library, but I will take a look at how they are doing it, thanks!
How can I copy BufferedInputStream to a file?
you can use http://clojure.java.io/copy
it can copy from anything that can be coerced to an input stream to something coercible to an output stream
so something like (<http://clojure.java.io/copy|clojure.java.io/copy> the-input-stream (<http://clojure.java.io/file|clojure.java.io/file> "foo"))
Ah right!
Thank you so much!
I've forgotten to wrap the path with io/file
What is the most beginner-friendly way to auto-reformat a snippet of Clojure code, that works with incomplete expressions? Could be an IDE or a standalone reformatter, but for newbies who may not have a complete setup or specialized skills. In a web page would be great, but looking for anything with close-to-zero setup or experience required.
incomplete how? including unbalanced parens?
Try parinfer, it’s included in Cursive
The point is to help people who have not yet installed something like Cursive. Incomplete as in unfinished -- may be missing closing brackets.
And cursive + IntellJ will format code. Indentation can be customised too
Parinfer will add brackets automatically. It’s what I use, has all the features of paredit but less stricter
Cursive is great once you've installed it, created a project, and figured out how to use it. This question is for people who have not yet done that, have a snippet of Clojure code, and want to auto-reformat it properly.
Found this https://web.archive.org/web/20170707155649/http://pretty-print.net:80/
http://pretty-print.net is exactly what I have in mind!! Thanks. But alas, it doesn't work. Paste in:
(defn foo
(swish
tepplo))
and hit "Format" and you get:
(defn foo)
Yes, what I tried was missing an argument list... but it should be formattable...
BTW I see that I did say "Could be an IDE" but I meant something that a total newbie could easily install and use right away. I use and love Cursive, but getting started does take some time and work and a learning curve, esp if you don't yet have Java installed or understand anything about projects, etc.
BTW also my current use case is students who are just starting and can use http://replit.com to write and evaluate code with zero setup (just a browser), but it doesn't do auto-formatting of Clojure which I've found to be essential in this context.
BTWx3, Calva is a little easier for total newbies than Cursive to set up and use, but can't auto-format incomplete expressions.
zprint looks promising since there are standalone executables, but I can't figure out how to make it work for newbies.