Welcome! Please see the About page for a little more info on how this works.

0 votes
in Syntax and reader by

I started learning Clojure. The tutorial asks to define constantly. I managed it using fn. How to do it using #()?

2 Answers

0 votes
edited by
Best answer

[edited due to missing # in answer]

It's kind of janky to go that route, since the intent of constantly is to allow a function of arbitrary arguments that always returns a constant. The #(...) anonymous function shorthand provides quick ways of defining anonymous functions working on various arities

0 - #(vector)
1- #(vector %1)
2 - #(vector %1 %2)
n - #(vector %1 %2 ... %n)
& args - #(apply vector %&)

There' some limitations though, since you are defining the body of the function....it will end up looking like an invocation if you try to define something like identity...

#(%) ;;this will try to invoke the single argument as if it were a unary function.

A way around this is to bind the input in a let or some other form to communicate the arity...

#(let [v %] v)


#(do %)

will work like identity, we just return it in the body.

We want to define a function of variable args though, so we want %& to indicate the args. Yet the args are meaningless for the result...the only communicate to the anonymous function sugar the number or arity of the args.

While awkward, this is one way to do it:

(defn constantly2 [v]
;;ignore the arguments, but communicate a varargs function
   #(let [_ %&]  
user=> (def f (constantly2 10))
user=> (f 1 2 3 4)
user=> (f 1)
user=> (f 5)
user=> (f :a)

I would probably avoid using the #(..) syntax sugar for these kinds of things. The shorthand is most useful for very simple inline anonymous functions, and even then I typically prefer to write out the fn explicitly.

Thanks. Figured it would be something awkward. I don't understand why #(%) is treated as a function evaluation. Does the reader see (%) first and try to evaluate it before parsing the #?
Pretty sure the [FnReader](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LispReader.java#L861) which is mapped to the dispatch reader for the \( character reads the form literally and constructs a `fn` form that is evaluated.
    user=> (read-string "#(+ % 2)")
    (fn* [p1__3#] (+ p1__3# 2))
    user=> (read-string "#(%)")
    (fn* [p1__6#] (p1__6#))
Thanks for the read-string function. Didn't know about that. Looks like the fn template into which #() expands has the extra () which requires that the form in #() can be evaluated.
In a nutshell, this is what I see going on (translated to clojure, missing some validation stuff):

(require '[clojure.walk :as w])
(require '[clojure.string :as s])

(def the-form '(+ 1 (* % 3)))

(def anons #{"%" "%&"})
(def multi #"%[0-9]*")
(defn infer-args [form]
  (->> form
       (tree-seq coll? seq)
        #(and (symbol? %)
              (or (anons (name %))
                  (re-find multi (name %)))

(defn derive-function [form]
  (let [args       (infer-args form)
        args->syms (zipmap args (repeatedly #(gensym "arg")))
        new-args   (vals args->syms)]
    `(fn [~@new-args] ~(w/postwalk-replace args->syms form))))

So the reader provides a clojure form, `(+ 1 (* % 3))`, as defined by the-form.  This form is inferred to be the "body" of a `fn` invocation, where the arguments are implied by the presence of any % prefixed symbols.  So we pass the body form to a function that scrapes it to determine args, then emits a `fn` form with the inferred args and the original body, where the raw args have been replaced by gensym'd ones.  Since the dispatch function is associated with `(`, we always have to pass a readable sexpr for the function body.  So there is no way to pass a function that returns a simple value directly with the established syntax.
+1 vote

To define constantly using the #() shortcut, I'd do this. Let's say I want to make a function that always returns 1.

I could do:

(constantly 1)

But to do it using #():

#(do %& 1)

%& is required to ensure that it works for any arity. But we want to ignore the arguments. do returns the value of the last expression, so in this case it will return 1.