Here are two of the ways to write functions in Clojure:
(defn foo [a b] (+ a b))
(fn foo [a b] (+ a b))
I can call them like so:
In the case of 'defn'
(foo 1 2)
In the case of 'fn'
((fn foo [a b] (+ a b)) 1 2)
'fn' doesn't seem to insert its optional name into the current scope, where 'defn' seems to do exactly that. Is there any other difference or reason for having two ways of creating functions? Is there a reason we don't just use 'fn' like this:
(fn foo [a b] (+ a b))
(foo 1 2)
defn
is basically defined as*:
(defmacro defn [name args & body]
`(def ~name (fn ~args ~@body)))
Or in other words, you could basically write:
(defn my-func [a]
(stuff a))
As*:
(def my-func
(fn [a] (stuff a)))
Using just fn
creates an anonymous function that alone isn't bound to any symbol externally. It must be bound using let
or def
to be referred to outside of itself.
By having defn
defined in terms of def
and fn
, the responsibilies of binding a function to a symbol (as well as all the other complexities that come with it) and handling function behaviour can be separated.
When you supply a name for fn
, it can't be referred to outside of the function, but it can be used to refer to itself to create a recursive anonymous function:
(fn my-func [n] (my-func (inc n))
And, it gives the function a slightly nicer name to show up in stack traces to ease debugging:
(defn my-func []
((fn my-inner-func [] (/ 1 0))))
=> #'digital-rain.core/my-func
(my-func)
java.lang.ArithmeticException: Divide by zero
at clojure.lang.Numbers.divide(Numbers.java:158)
at clojure.lang.Numbers.divide(Numbers.java:3808)
at digital_rain.core$my_func$my_inner_func__2320.invoke(form-init1838550899342340522.clj:2)
at digital_rain.core$my_func.invokeStatic(form-init1838550899342340522.clj:2)
at digital_rain.core$my_func.invoke(form-init1838550899342340522.clj:1)
*
These are gross understatements and a tad misleading, but they simplify things.
In reality, defn
isn't defined using defmacro
; defmacro
is actually defined using defn
. defn
also adds some good stuff like pre/post condition checking, documentation, and other meta information; but that's not as relevant here. I recommend looking over its source for a more in-depth look; although it's pretty convoluted. The fundamental guts of clojure.core
can be a little daunting to wrap your head around.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With