clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
Arun Mascarenhas 2021-06-05T02:16:42.449800Z

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.

phronmophobic 2021-06-05T02:29:39.449900Z

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)]
    (-&gt; v
        meta
        :arglists)))

&gt; (fn-arglists +)
;; ([] [x] [x y] [x y &amp; more])

Arun Mascarenhas 2021-06-05T02:32:36.450100Z

> macros only receive the syntax/code (ie. `f` in this case). Ah! I did not realize that. Thank you for pointing that out.

Arun Mascarenhas 2021-06-05T02:34:08.450300Z

I do have another, not guaranteed, way to get arglists based on reflection. Your function, however, is very interesting.

Arun Mascarenhas 2021-06-05T02:34:56.450500Z

Your response has taught me a lot. Thank you!

👍 1
phronmophobic 2021-06-05T02:35:58.450800Z

How does the reflection technique work? That sounds interesting.

Arun Mascarenhas 2021-06-05T02:36:23.451200Z

Give me a minute to dig up the code.

Arun Mascarenhas 2021-06-05T02:41:01.451600Z

(defn- non-defn-&gt;arities
  [f]
  (let [arity-info (-&gt;&gt; f
                        class
                        .getDeclaredMethods
                        (map (fn [x] method-&gt;name-and-arity x))
                        (apply merge-with into)
                        (methods-&gt;arities f))]
    arity-info))

(defn- methods-&gt;arities
  [m arities]
  (let [arity-list (sort &lt; (: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-&gt;name-and-arity
  [m]
  {(keyword (.getName m)) [(method-&gt;arity m)]})

(defn- method-&gt;arity
  [m]
  (-&gt;&gt; m .getParameterTypes alength))

Arun Mascarenhas 2021-06-05T02:41:51.451800Z

It's a little bit of a mess, but that is the gist of the code.

Arun Mascarenhas 2021-06-05T02:42:58.452Z

It was inspired by the discussion here: https://stackoverflow.com/questions/1696693/clojure-how-to-find-out-the-arity-of-function-at-runtime

phronmophobic 2021-06-05T02:43:13.452300Z

cool!

km 2021-06-05T03:06:23.452500Z

Hey guys, Anyone have an easy way to test multipart/form-data requests with ring-mock?

Andrew Lai 2021-06-07T13:41:33.044900Z

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 .

Vlad 2021-06-05T08:15:08.453Z

what is the best way to parse dates from CSV in Clojure as it stands?

borkdude 2021-06-05T09:02:03.453500Z

@cstmlcodes CSV is just text, so no different than parsing dates from any other string

javahippie 2021-06-05T11:14:09.454700Z

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?

raspasov 2021-06-05T11:21:54.454900Z

There are Clojure libraries available that implement rate-limits/circuit breakers.

raspasov 2021-06-05T11:23:13.455100Z

https://github.com/josephwilk/circuit-breaker https://github.com/brunoV/throttler (I haven’t used them, but probably worth a look)

javahippie 2021-06-05T11:24:30.455700Z

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!

👍 1
Karol Wójcik 2021-06-05T12:18:42.456200Z

How can I copy BufferedInputStream to a file?

alexmiller 2021-06-05T12:22:49.456500Z

you can use http://clojure.java.io/copy

alexmiller 2021-06-05T12:23:48.457200Z

it can copy from anything that can be coerced to an input stream to something coercible to an output stream

alexmiller 2021-06-05T12:24:31.458200Z

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

Karol Wójcik 2021-06-05T12:24:33.458300Z

Ah right!

Karol Wójcik 2021-06-05T12:24:41.458600Z

Thank you so much!

Karol Wójcik 2021-06-05T12:24:53.458900Z

I've forgotten to wrap the path with io/file

lspector 2021-06-05T13:38:06.461700Z

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.

2021-06-05T13:40:39.461800Z

incomplete how? including unbalanced parens?

2021-06-05T13:41:28.462Z

https://github.com/kkinnear/zprint

indy 2021-06-05T14:46:46.463Z

Try parinfer, it’s included in Cursive

lspector 2021-06-05T14:47:31.463700Z

The point is to help people who have not yet installed something like Cursive. Incomplete as in unfinished -- may be missing closing brackets.

indy 2021-06-05T14:48:35.465200Z

And cursive + IntellJ will format code. Indentation can be customised too

indy 2021-06-05T14:49:40.466800Z

Parinfer will add brackets automatically. It’s what I use, has all the features of paredit but less stricter

lspector 2021-06-05T14:51:21.467Z

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.

lspector 2021-06-05T14:58:46.467700Z

http://pretty-print.net is exactly what I have in mind!! Thanks. But alas, it doesn't work. Paste in:

(defn foo
(swish
tepplo))

lspector 2021-06-05T14:59:19.467900Z

and hit "Format" and you get:

(defn foo)

lspector 2021-06-05T14:59:49.468100Z

Yes, what I tried was missing an argument list... but it should be formattable...

lspector 2021-06-05T15:01:45.468300Z

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.

lspector 2021-06-05T15:03:05.468600Z

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.

lspector 2021-06-05T15:04:50.468800Z

BTWx3, Calva is a little easier for total newbies than Cursive to set up and use, but can't auto-format incomplete expressions.

lspector 2021-06-05T15:12:16.469100Z

zprint looks promising since there are standalone executables, but I can't figure out how to make it work for newbies.