3
votes

I wanted a function literal in Clojure that can take any number of arguments, but doesn't actually use them. So I've discovered %& ('rest arg' for function literal) and #_ ('discard' reader macro).

But how they work together surprises me:

=> (macroexpand `#(... #_ %&))
(fn* [& rest__125459__125460__auto__] (...))

This looks like what I wanted (and seems to work in the end), but is it how the discard macro supposed to work? The doc says:

The form following #_ is completely skipped by the reader.

But apparently here the ignored %& form has a side-effect, that is it affects the function literal args list. Should I rely on this behavior or does it look more like a bug?

3

3 Answers

2
votes

The discard reader macro #_ is not intended to be used alone. Here you can see it in action:

Before:

(println "first")
(do
  (print "second ")
  (dotimes [i 5]
    (print i " "))
  (newline))
(println "third")

first
second 0  1  2  3  4  
third

and after:

(println "first")
#_(do
    (print "second ")
    (dotimes [i 5]
      (print i " "))
    (newline))
(println "third")

first
third

So adding #_ (recursively) discards everything inside the form it is applied to.

Regarding your original question of ignoring arguments, you have a few choices:

(mapv  #(do %& 42)        (range 3)) => [42 42 42]
(mapv   (fn [& _]   42)   (range 3)) => [42 42 42]
(mapv   (constantly 42)   (range 3)) => [42 42 42]
0
votes

I spoke too soon, probably not a bug, maybe it should be disallowed and give an error. But really #_ is for discarding forms, and %& is not a form but also a reader macro.

So #_%& is just like #_@, #_; and doesn't work. The discard reader #_ is only to be used with "real forms" like () or [] (etc)

0
votes

#_ clause is applied only to a form. In your example, there is a space after it so it doesn't have any effect.

Prepend any list/form with #_ to ignore it completely:

#_(do (print 42) (/ 0 0)) ;; there won't be an error