# Named let or recursive let

**URL:** <https://discourse.julialang.org/t/named-let-or-recursive-let/118231>\
**Category:** Internals & Design\
**Created:** [August 15, 2024, 5:12pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231 "2024-08-15T17:12:12Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![tobia](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobia/32/211355_2.png) [@tobia](https://discourse.julialang.org/u/tobia)\
**Post date:** [August 15, 2024, 5:12pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231/1 "2024-08-15T17:12:12Z")

</div>

One of the features I miss from Scheme is the “named let.” It’s a let expression that allows its own body to invoke itself recursively, by giving it a name that can be used to run another instance of the body on the call stack, with new values for the variables.

For example, the following Scheme code defines the variable fib10 (the 10th Fibonacci number) by using a named let that starts with n=10 and will call itself recursively until the computation is done:

```scheme
(define fib10
  (let loop ((n 10))
    (if (<= n 1) 
        1
        (+ (loop (- n 1))
           (loop (- n 2))))))

```

The most straightforward translation to Julia would be a recursive function defined inside a local scope and invoked rightaway:

```julia
fib10 = let
	function loop(n = 10)
		if n ≤ 1
			1
		else
			loop(n-1) + loop(n-2)
		end
	end
	loop()
end

```

But I think that the named let could be added to Julia as a let statement where the variable initialization section is given a name, in the style of function parameters, that can then be invoked from within the body:

```julia
# proposed "named let" syntax:

fib10 = let loop(n = 10)
	if n ≤ 1
		1
	else
		loop(n-1) + loop(n-2)
	end
end

```

This syntax is currently rejected by the compiler, so it would not break any existing code.

I, for one, would use it very often, namely every time I need to express a computation structure that is branching, or is more complex than a simple “while” or “for” loop.

What do you think?

PS. I’ll probably make my own macro that approximates this syntax, but I think it’s general and useful enough to propose for inclusion in the language.

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [August 15, 2024, 5:43pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231/2 "2024-08-15T17:43:06Z")

</div>

> PS. I’ll probably make my own macro that approximates this syntax, but I think it’s general and useful enough to propose for inclusion in the language.

This is the right idea. Given that some schemes already implement `let` as a macro on top of `lambda`, I’d suggest doing the same in Julia 🤷

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [August 15, 2024, 5:43pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231/3 "2024-08-15T17:43:35Z")

</div>

From how you’re describing it, the named let really does sound like a closure in Julia and your macro seems sound. I don’t know Scheme so I don’t know what its distinction between named lets and nested functions are (which exist as far as I can Google), but if there is one besides calling it right away, then that’d be an issue.

Recursive closures currently have type inference issues though because whether the captured variable has an inferrable type or an uninferrable `Core.Box` is currently determined at parsing, far before any type inference in a JIT-compiled call. There have been discourse musings on how it could be improved, but AFAIK there hasn’t been work done on it. A simple demo with your function:

```julia
julia> function loop(n = 10)
               if n ≤ 1
                   1
               else
                   loop(n-1) + loop(n-2)
               end
           end
loop (generic function with 2 methods)

julia> @code_warntype loop()
MethodInstance for loop()
  from loop() @ Main REPL[1]:1
Arguments
  #self#::Core.Const(loop)
Body::Int64
1 ─ %1 = (#self#)(10)::Int64
└── return %1

julia> letloop = let
       function loop(n = 10)
                      if n ≤ 1
                          1
                      else
                          loop(n-1) + loop(n-2)
                      end
                  end
       end
(::var"#loop#1") (generic function with 2 methods)

julia> @code_warntype letloop()
MethodInstance for (::var"#loop#1")()
  from (::var"#loop#1")() @ Main REPL[3]:2
Arguments
  #self#::var"#loop#1"
Body::Any
1 ─ %1 = (#self#)(10)::Any
└── return %1

```

Possible workaround is to evaluate the recursive function but with a generated name to the global scope, which closures do now anyway, but that gets in the way of capturing other variables.

---

<div class="post-metadata">

**Author:** ![tobia](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobia/32/211355_2.png) [@tobia](https://discourse.julialang.org/u/tobia)\
**Post date:** [August 15, 2024, 5:52pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231/4 "2024-08-15T17:52:21Z")

</div>

I decided to use the regular `function` syntax for my macro argument, otherwise I would have to parse a name, a list of variable definitions, a body, and turn it all back into a function anyways.

So I came up with this:

```julia
macro call(f)
	if !isa(f, Expr) || f.head != :function
		error("Usage: @call function(...) ... end")
	end
	fName = f.args[1].args[1]
	:(let
		$f
		$fName()
	end)
end

```

Example usage:

```julia
fib10b = @call function loop(n = 10)
	if n ≤ 1
		1
	else
		loop(n-1) + loop(n-2)
	end
end

```

Still, if others find it useful, it could be considered as an addition to the language, with the named let syntax in the OP.

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [August 15, 2024, 6:11pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231/5 "2024-08-15T18:11:21Z")

</div>

That seems reasonable.

You can add type declarations to macro parameters. So you don’t need the `isa` check:

```julia
macro call(f::Expr)
...

```

And you can apply the body of the function directly:

```julia
:(let; ($f)() end)

```

Now that I think about it, you could also do that in your original example, though it only saves you typing the function name once.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [August 15, 2024, 10:11pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231/6 "2024-08-15T22:11:17Z")

</div>

> [@tobia](#):
>
> an addition to the language

Can be immediately available as a package, and this is what the successful syntax extensions do. The trend is toward separation into optionally loaded code, even trimming the sysimage.

---

<div class="post-metadata">

**Author:** ![tobia](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobia/32/211355_2.png) [@tobia](https://discourse.julialang.org/u/tobia)\
**Post date:** [August 16, 2024, 5:22pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231/7 "2024-08-16T17:22:37Z")

</div>

Yes, I realized right after typing it. I can just use this:

```julia
fib10 = (function loop(n = 10)
	if n ≤ 1
		1
	else
		loop(n-1) + loop(n-2)
	end
end)()

```

So unless one of the core developers is also a fan of Scheme’s named let, and decides to add something like `let loop(n = 10) ... end` to the core language, I’ll just use this version.

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [August 16, 2024, 7:33pm UTC](https://discourse.julialang.org/t/named-let-or-recursive-let/118231/8 "2024-08-16T19:33:31Z")

</div>

Note that `let` introduces a new scope, but parentheses don’t. So `loop` would remain in scope after calling it. That’s probably fine in most cases, just something to keep in mind.
