Search code examples
clojure

What is the difference between fn an defn in clojure?


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)

Solution

  • 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.