Fork me on GitHub
#clojuredesign-podcast
<
2020-10-21
>
jrwdunham16:10:06

letfn lets you define recursive functions while (let [my-fn (fn ...)]) does not — one of the benefits of letfn that occurred to me while listening to your let cast. Enjoyed it. Thanks guys!

nate17:10:19

oh, that's cool!

nate17:10:26

thanks for pointing that out.

sharkdance 3
nate17:10:58

from : > All of the names are available in all of the definitions of the functions, as well as the body.

nate17:10:00

very cool

lodin20:10:07

Anonymous functions can be recursive. Most of the time you can use recur. If you have multiple arities you can name the anonymous (!) function, e.g. (fn my-func ([x] (my-func x nil)) ([x y] ...)). You only need let-fn when you want local mutually recursive functions. And even then you could hack around it using atoms (which is essentially what Clojure does at the top level, when you use declare). Example:

(let [odd? (atom nil)
      even? (fn even? [n]
              {:pre [(integer? n)]}
              (cond
                (pos? n) (@odd? (dec n))
                (zero? n) true
                :else (even? (- n))))
      _ (reset! odd? (fn [n]
                       (cond
                         (zero? n) false
                         :else (even? (dec n)))))
      odd? @odd?]
  [(map (complement odd?) (range -3 (inc 3)))
   (map even? (range -3 (inc 3)))])

lodin20:10:07

And if you want to go crazy, you can use the Y combinator for mutually recursive functions, like so:

(let [[even? odd?] (Y* (fn [even? odd?]
                         (fn [n]
                           {:pre [(integer? n)]}
                           (cond
                             (pos? n) (odd? (dec n))
                             (zero? n) true
                             :else (even? (- n)))))
                       (fn [even? odd?]
                         (fn [n]
                           (cond
                             (zero? n) false
                             :else (even? (dec n))))))]
  [(map (complement odd?) (range -3 (inc 3)))
   (map even? (range -3 (inc 3)))])

lodin20:10:42

And the Y* function uses no magic or atoms, even though it is incomprehensible. 🙂 (You can just google "y combinator mutual recursion" to find a version of it.)

nate21:10:37

Woah, @U8J6W2SC8 that's some intense code. thanks for posting it

nate21:10:55

interesting point about mutual recursion

nate21:10:16

I haven't needed to do that much so far, but I'll try to remember it next time I do

nate21:10:26

reminds me of the trampoline function

lodin05:10:31

@U0510902N I don't even remember when I last needed that. And yeah, trampolining is probably a good idea for any recursion that doesn't use recur since there's no tail call optimization. It's been a long time since I needed trampoline as well. 🙂