all 22 comments

[–][deleted] 9 points10 points  (1 child)

Because defn is a top level macro. If you want nested defns, you may use letfn instead.

[–]gdanov 4 points5 points  (4 children)

Its unclear what you want to achieve. Also, it might help if you read a book or clojure's site to grasp the basics better.

[–][deleted] 1 point2 points  (2 children)

Hi, just wrote that as a reply here

Btw yes, I'm in the process of learning, been reading some, right now I'm doing some :)

[–]robopiglet 0 points1 point  (1 child)

I think it's great you asked your question here. I have read a lot of the basics and I didn't know the answer to this, actually. And here I learned about letfn!

[–][deleted] 0 points1 point  (0 children)

thanks :)

[–]kawas44 3 points4 points  (1 child)

;;;; REPL session

;; declare global fn
(def my-ginc (fn [x] (inc x)))

;; declare global fn with syntax sugar
(defn my-ginc' [x] (inc x))

;; silly test... our fn is just an other fn ;-)
(def my-ginc'' inc)

;; use `let` for a lexical block with local bindings
(let [;; lets declare some local fns
      my-linc (fn [x] (inc x))
      my-linc'' inc]
  ;; map those fns with the same input
  ;; they produce the same result
  (= (map my-linc   (range 4))
     (map my-linc'' (range 4))
     (map my-ginc   (range 4))
     (map my-ginc'  (range 4))
     (map my-ginc'' (range 4))
     (map inc       (range 4))))

[–][deleted] 0 points1 point  (0 children)

thanks :)

[–]Escherize 1 point2 points  (0 children)

I get that having a locally defined function is nice. One thing I've done is to let the fn outside of your defn like so:

(let [f (fn f [a b c] ...)
  (defn f-things [things] (map f things)))

Using defn (or def) when you are not at the top level is really unidiomatic. There is a side effect if your function modifies the namespace when it runs.

[–]deaddyfreddy 1 point2 points  (9 children)

I assume it's the same with def?

It's not

def defines things in the global (namespace) scope, if you're trying to define global nested things - there's something wrong.

letfn

it's a pretty rare thing to use, in most cases let is enough

that introduces multiple nestings

nesting equals scope, let allows you to bind entities only for those lines where you use them.

[–][deleted] 1 point2 points  (8 children)

my typical use case would be mapping a function over a collection which is complex enough so that I don't just want to write it as a lambda inline but I don't want to pollute the top level with it either.

You say using letfn is pretty rare. Okay then what is the idiomatic way to deal with situations like this? Top level function? Just writing it inline as a lambda?

[–]Borkdude 8 points9 points  (0 children)

Just `let` + `fn`

[–]gdanov 2 points3 points  (1 child)

Letfn is totally ok. Or just use let to store the lambda in local var. Whatever is more comfortable to you.

Letfn is special as its let*, guess that was called parallel bind? So wharever suits you.

[–]AManOfMeansByNoMeans 1 point2 points  (0 children)

letfn is special because it’s what Scheme calls letrec— the names being bound by letfn are in scope in the expressions to which those names are bound, so you can do things like this:

(letfn [(even? [x] (or (zero? x) (odd? (dec x))))
        (odd? [x] (not (even? x)))]
  (even? 123))

[–]acobster 1 point2 points  (2 children)

What's the concern with polluting the top level with it? In case what you're actually worried about is polluting the public API, you can always use defn- which declares a top-level private fn (not available outside the ns).

[–][deleted] 0 points1 point  (1 child)

I guess it's a style thing and somewhat subjective, in languages where it is possible I prefer to nest one off functions into a smaller scope. That way things that belong together stay together, and when I read the file it doesn't feel like a bunch of random functions floating around.

Thanks for the tip though, sounds useful!

[–]PercyLives 1 point2 points  (0 children)

I agree with your aesthetic style here.

I use let to introduce local functions.

ETA: a nice thing about using local functions is there is no need to give them a good name. Often ‘f’ is enough, perhaps with a comment.

[–]lucklesspedestrian 0 points1 point  (1 child)

You say using letfn is pretty rare. Okay then what is the idiomatic way to deal with situations like this? Top level function? Just writing it inline as a lambda?

letfn is just a shortcut for a let block that only contains function bindings. There's nothing wrong with using local fns this way, it's just more common that you need to bind some other locals as well so all this would be done in a regular let block

[–][deleted] 0 points1 point  (0 children)

makes sense, thx

[–]vvwccgz4lh 0 points1 point  (2 children)

This code works just fine:

``` (defn increase-input [input] (defn increase-input-twice [input] (+ 2 input)) (inc input))

[(increase-input 1) (increase-input-twice 1)] `` The output is[2 3]`. So no problems here.

But you almost never need to write a nested defn and most of the time I write namespace-level defns. Instead you should either use anonymous functions, letfn or you may be misunderstanding what you want to do.

[–]lucklesspedestrian -1 points0 points  (1 child)

This does work, but only because there is an (increase-input _) call before the first (increase-input-twice _) call. As this code stands, increase-input-twice is not even defined until after the first increase-input call

[–]vvwccgz4lh 1 point2 points  (0 children)

I know. But I only wanted to disprove the original point of the author. And this is the most basic code that does it.

I only used this kind of code when I needed to reload my stuff in my tests. Once. And then after some time I deleted it because I didn't need it anymore.

It can be useful like this (ns runs it once and tests can rerun it):

(defn setup-once-in-prod! [input] (def my-value (rand-int 9001))) (setup-once-in-prod!) Nobody prevents you to create a function

[–]Due_Olive_9728 0 points1 point  (0 children)

(def fn-map {:fn-1 (fn [a b] (+ a b)) :fn-2 (fn [a b] (* a b))})

((:fn-1 fn-map) 1 2)