Why can't I bind + in clojure?

Can anyone explain why I can rebind list but not +?

(binding [list vector]
  (list 1 3))
(binding [list +]
  (list 1 3))
(binding [+ list]
  (+ 1 3))

I'd like to rebind + so I can do partial evaluation.

Answers


In Clojure 1.1.0 at least, + with two arguments is inlined for performance. Your binding happens too late. With more arguments it works differently.

Clojure 1.1.0-master-SNAPSHOT
user=> (binding [+ -] (+ 1 2))
3
user=> (binding [+ -] (+ 1 2 3))
-4

One workaround is to make your own namespace and shadow clojure.core/+ with your own function.

user=> (ns foo (:refer-clojure :exclude [+]))
nil
foo=> (defn + [& args] (reduce clojure.core/+ args))
#'foo/+
foo=> (+ 1 2)
3
foo=> (binding [+ -] (+ 1 2))
-1

Note that inlining appears to happen even more aggressively in the current snapshot of Clojure 1.2.0.

Clojure 1.2.0-master-SNAPSHOT
user=> (binding [+ -] (+ 1 2))
3
user=> (binding [+ -] (+ 1 2 3))
6

It may be wisest to use a function name other than +, e.g. add, to avoid confusion.


Quick workaround: use let instead of binding and this will work for you just fine:

user=> (let [+ list] (+ 2 3))
(2 3)

A little (incomplete) digging into the reason:

Take a look at the source for the + function:

(defn +
  "Returns the sum of nums. (+) returns 0."
  {:inline (fn [x y] `(. clojure.lang.Numbers (add ~x ~y)))
   :inline-arities #{2}}
  ([] 0)
  ([x] (cast Number x))
  ([x y] (. clojure.lang.Numbers (add x y)))
  ([x y & more]
   (reduce + (+ x y) more)))

Notice that there are several inline function definitions for different numbers of arguments. If you try to rebind the 0 or 1 arity definitions, it works just fine:

user=> (binding [+ (fn [] "foo")] (+))
"foo"
user=> (binding [+ (fn [a] (list a))] (+ 1))
(1)

Now, this definitely doesn't work (as you discovered) for the 2-argument case. I'm not quite connecting the dots, but the . (special form) makes me suspicious combined with binding being a macro whereas let is a special form...

The metadata specially calling out arity 2 also seems suspicious.


Need Your Help

Generating api keys for REST api

php codeigniter rest api-key

Am using phil sturgeon codeigniter REST library for my REST api, i have set up the api to use api keys in the config file and also created a table for storing the keys on my database.

Allow play rtmp from one site (nginx with rtmp module)

linux nginx live vps rtmp

is there any way to deny users play my stream on other sites? I'm running nginx with rtmp module, ubuntu 13. Here is my ngingx.conf

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.