Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure style: defn- vs. letfn

Clojure style (and good software engineering in general) puts emphasis on lots of small functions, a subset of which are publicly visible to provide an external interface.

In Clojure there seem to be a couple of ways to do this:

(letfn [(private-a ...)
        (private-b ...)]
  (defn public-a ...)
  (defn public-b ...))

(defn- private-a ...)
(defn- private-b ...)
(defn public-a ...)
(defn public-b ...)

The letfn form seems more verbose and perhaps less flexible, but it reduces the scope of the functions.

My guess is that letfn is intended only for use inside other forms, when little helper functions are used in only a small area. Is this the consensus? Should letfn ever be used at a top level (as I've seen recommended before)? When should it be used?

like image 416
rybern Avatar asked Apr 23 '14 21:04

rybern


3 Answers

letfn is intended for use in cases of mutual recursion:

(letfn [(is-even? [n]
          (if (zero? n)
            true
            (is-odd? (dec n))))
        (is-odd? [n]
          (if (zero? n)
            false
            (is-even? (dec n))))]
  (is-even? 42))

;; => true

Don't use it at the top level.

Also don't use the defn macro anywhere else than at the top level unless you have very specific reasons. It will be expanded to the def special form which will create and intern global var.

like image 188
Leon Grapenthin Avatar answered Nov 12 '22 18:11

Leon Grapenthin


The purpose of letfn is totally different from the purpose of defn form. Using letfn at the top level does not give you same properties of defn, since any bindings of names to functions bound inside letfn is not visible outside its scope. The binding for functions bound inside let or letfn is not available outside its lexical scope. Also, the visibility of functions bound inside letfn is independent of the order in which they are bound inside that lexical scope. That is not the case with let.

like image 30
grdvnl Avatar answered Nov 12 '22 20:11

grdvnl


My rules are these:

  • If a subsidiary function is used in one public one, define it locally with let or letfn.
  • If it is used in several, define it at top level with defn-.

And

  • Don't use let or letfn at top level.
  • Don't use def or defn or defn- anywhere other than top level.
like image 34
Thumbnail Avatar answered Nov 12 '22 18:11

Thumbnail