# Help needed: Building an intuitive understanding of closures, scoping and type-stability

**URL:** <https://discourse.julialang.org/t/help-needed-building-an-intuitive-understanding-of-closures-scoping-and-type-stability/107960>\
**Category:** New to Julia\
**Tags:** type-stability, closure\
**Created:** [December 22, 2023, 9:29pm UTC](https://discourse.julialang.org/t/help-needed-building-an-intuitive-understanding-of-closures-scoping-and-type-stability/107960 "2023-12-22T21:29:36Z")\
**Posts on this page:** 1\
**Showing post:** 3

<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:** [December 23, 2023, 12:37am UTC](https://discourse.julialang.org/t/help-needed-building-an-intuitive-understanding-of-closures-scoping-and-type-stability/107960/3 "2023-12-23T00:37:31Z")

</div>

A globally scoped function accessing a global variable is not generally considered a closure; many languages with the former lack the latter. `i` is bound to the only closure in your examples.

> [@Bizzi](#):
>
> ```julia
> const st_const = st #Maybe if we make st a const, it will be type-stable? 
> g(x,p) = C(x,p,st_const) 
> @code_warntype g(x,ps) #No :(
> 
> ```

This doesn’t work because `C` isn’t `const`. It didn’t matter for the `@code_warntype C(...` line because neither the callable nor arguments need to be assigned to `const` variables for the runtime dispatch and type inference, in fact dispatch is done with respect to the callable _and_ the arguments.

> [@Bizzi](#):
>
> It’s been hard for me to “think like a compiler”.

The wider compiler is pretty complex, but you can definitely follow the type inference part, occasionally even do better. Unfortunately you don’t have direct control over the type inference process, so when that happens, the most you can do is manually annotate some variables to give the compiler more hints.

> [@Bizzi](#):
>
> As far as I can tell, the let statements kind of enforce value semantics by means of copies, which is what I want basically 100% of the time. Can’t I achieve the same thing without the boilerplate?

It doesn’t do value semantics, it’s still capturing a variable, only the `let st = st` part made a new `st` variable that is local to the `let` block and never reassigned. If you had reassigned it, the compiler can’t infer it anymore even if all reassignments occurred prior to capturing and didn’t even change the type; that is one example of how you could do type inference better than the compiler. The only way to “capture by value” is to directly store the value in a `struct` instance, which you can make [callable](https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects).

If you want to make a global variable inferrable, the easiest way is to make it `const`, promising it’ll never be reassigned, so it’ll always have the same type. The 2nd easiest way is annotating the variable with a concrete type, much like how you annotate `struct` fields for type stability. Reassignment works if the values can be automatically `convert`ed to the annotated type, which isn’t necessarily concrete; if you have some unexpected type inference problems check if you unintentionally annotated an abstract type. There isn’t a provided way to automatically annotate the concrete type from the right-hand value, but there’s [a macro with an atypical use of the `local` statement that accomplishes that](https://discourse.julialang.org/t/seven-lines-of-julia-examples-sought/50416/157). Unlike a `const` variable whose value can often be inlined, the typed global variable’s value is retrieved through a couple dereferences, but that bit of overhead is well worth the type stability.

---

_[View the full topic](https://discourse.julialang.org/t/help-needed-building-an-intuitive-understanding-of-closures-scoping-and-type-stability/107960)._
