Search code examples
functionclojuremacrosspecial-form

Clojure asterisks special forms (fn*, let*, etc...)


I discovered that a lot of "special forms" are just macros that use their asterisks version in the background (fn*, let* and all the others).

In case of fn, for example, it adds the destructuring capability into the mix which fn* alone does not provide. I tried to find some detailed documentation of what fn* can and can't do on its own, but I was not so lucky.

It definitely supports:

  • the &/catchall indicator

    (fn* [x & rest] (do-smth-here...))
    
  • and curiously also the overloading on arity, as in:

    (fn* ([x] (smth-with-one-arg ...) 
         ([x y] (smth-with-two-args ...))
    

So my question finally is, why not only define:

(fn& [& all-args] ...)

which would be absolutely minimal and could provide all the arity selection via macros (checking size of parameter list, if/case statement to direct code path, bind first few parameters to desired symbol, etc..).

Is this for performance reasons? Maybe someone even has a link to the actual standard definition of the asterisks special forms handy.


Solution

  • Yes, you could do all that as macros on top of the ultra-primitive fn& that you propose, and it would certainly simplify the compiler implementation. The reason this is not done is partly for performance reasons (it would be rather slow, and the JVM already has a fast facility for dispatching based on arity), and partly "cosmetic": it means that each arity of a function is a different method of the JVM class that a function is compiled down to, which makes stacktraces nicer. This also helps the JIT "understand" our functions better, so that it can optimize accordingly.