Boxing obvious cases where the type is clear

Not so for closures, which is what you’re doing here. An excerpt from the link:

A language implementation cannot easily support full closures if its run-time memory model allocates all automatic variables on a linear stack. In such languages, a function’s automatic local variables are deallocated when the function returns. However, a closure requires that the free variables it references survive the enclosing function’s execution. Therefore, those variables must be allocated so that they persist until no longer needed, typically via heap allocation, rather than on the stack, and their lifetime must be managed so they survive until all closures referencing them are no longer in use.

It might appear that the do block closure only executes within the enclosing function’s scope and thus does not need to allocate outside its stack, but in general a higher order function could cache the closure somewhere that outlives the scope, so there’s no way around this.

The type inference limitation is orthogonal to and would arise under both static and dynamic typing. I’ve written about this before but long story short, it’s obviously impossible to infer a finite set of types for a captured variable that is reassigned by a closure because a method can be compiled for infinitely many call signatures and thus assign instances of infinitely many types to the captured variable. Hypothetically it’s possible that a typed box can be used for the variables you annotated, but right now it’s stored in an untyped Core.Box, and type conversions and assertions are inserted to enforce its annotated type.

For now, there’s a couple things you can do.

  1. Refactor to storing the captured data in Ref{T} and mutate that instead of reassigning the variable directly. If you don’t ever reassign the variable, the captured variable will be inferred as Ref{T} and the contents inferred as T. This isn’t conceptually different from annotating and reassigning the variable, but it’s better than a Core.Box surrounded by type conversions and assertions.
  1. Refactor so the variables aren’t captured, but provided to the closure as arguments. It could be easier to avoid unintended captures if you wrote a globally scoped function to pass into open instead of a locally scoped closure via do block. This is probably the cleaner option given how large and nested these do blocks are.
1 Like