Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between fn an defn in clojure?

Tags:

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)
like image 295
LunchMarble Avatar asked Mar 09 '18 01:03

LunchMarble


1 Answers

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.

like image 174
Carcigenicate Avatar answered Sep 30 '22 18:09

Carcigenicate