What is fn* and how to use it?

I looked into fn* since I was trying to generate code for #(println %), fipp’s author told me to look into:

It was quite interesting to see fn*. However it seems quite strange when I try using fn* in REPL:

cljs.user=> (fn* [] 1)
#object[Function]
cljs.user=> ((fn* [] 1))
1
cljs.user=> ((fn* [] %1) 3)
                     ⬆
WARNING: Use of undeclared Var cljs.user/%1 at line 1
nil
cljs.user=> ((fn* [] %) 3)
                     ⬆
WARNING: Use of undeclared Var cljs.user/% at line 1
nil

what does it mean?

2 Likes

found old question on mailing list:

https://groups.google.com/forum/?fromgroups=#!msg/clojure/f8iovY57J4k/gO8lhZVq8PEJ

fn* is a compiler intrinsic…it’s pretty low-level, it doesn’t
support destructuring,. So instead, core.clj creates a macro called fn
that adds all this other functionality and eventually spits out the
fn* in a format the compiler wants. Basically it’s set up this way so
that you can write the majority of the functionality behind fn can be
written in clojure code instead of in Java.
Note: there are other forms that are the same as fn*. Namely, loop*,
and let*. They all exist for the same reasons.

Btw, the % and %1 placeholders are only available inside the #() form. You can use parameters within fn* like so:

((fn* [x] x) 1)
;; => 1
1 Like

fn* is a special form that translates directly into host platform representation.
Here’s for example a set of special forms in ClojureScript’s compiler https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/analyzer.cljc#L1170-L1171
And here’s how analyzer parses fn* form and turns it into AST https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/analyzer.cljc#L1719-L1791

1 Like

btw how do you search symbols on GitHub that contains a *?

what fn* differs from fn?

Check out @roman01la’s answer above. The short answer is that fn handles argument destructuring and such, then emits a call to fn*.

1 Like