Is there a way to create a named function in clojure which is only visible in function scope?

StackOverflow https://stackoverflow.com/questions/22849110

  •  27-06-2023
  •  | 
  •  

Question

In Scheme I can do something like this:

(define (adder)
  (define (one) 1)
  (define (two) 2)
  (+ (one) (two)))

Calling adder results in 3 while calling one will yield an error since one is only visible within the scope of adder.

In Clojure if I do something similar

(defn adder []
  (defn one [] 1)
  (defn two [] 2)
  (+ (one) (two)))

one and two will pollute my namespace since defn uses def internally which creates bindings in the current namespace.

Is there a function/macro which creates named functions in local scope?

The reason for my question is that I got used to the way Scheme works. Naming my local functions that way often makes my code more readable.

Was it helpful?

Solution

Try letfn:

Takes a vector of function specs and a body, and generates a set of bindings of functions to their names. All of the names are available in all of the definitions of the functions, as well as the body.

 (defn adder []
   (letfn [(one [] 1)
           (two [] 2)]
     (+ (one) (two))))

OTHER TIPS

Additionally to Alex's excellent answer, any fn can be named.

(defn adder []
  (let [one (fn [] 1)
        two (fn [] (+ (one) (one)))]
    (+ (one) (two))))

This is useful if you already have a let block.

If an fn refers to itself, it needs a name of its own

(defn silly []
  (let [constant 5
        thing (fn thong
                ([a] (+ a constant))
                ([] (inc (thong constant))))]
    (* (thing) (thing))))

The name the fn is bound to need not be the same as the name it knows itself by.

If you want a function that is visible to the current namespace but not visible by other namespaces - you can use defn-

defn-
macro
Usage: (defn- name & decls)
same as defn, yielding non-public def

from http://clojuredocs.org/clojure_core/clojure.core/defn-

user=> (ns test)
nil

test=> (defn- foo [] "World!")
#'test/foo

test=> (defn bar [] (str "Hello " (foo)))
#'test/bar

test=> (foo)
"World!"
test=> (bar)
"Hello World!"
test=> (ns playground)
nil
playground=> (test/bar)
"Hello World!"

;; Error will be thrown
;; var: #'test/foo is not public
playground=> (test/foo)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top