30 秒でわかる
A Jane Street presenter continues a series on local types in the firm’s OCaml compiler branch. Using identity and iteration functions, the episode explains the hidden regional treatment of local parameters, tail calls and per-iteration regions.
- 1An apparent escapeAn identity function can accept and return a local string, despite the rule that local values must not escape their region.
- 2The hidden regional modeA local parameter is regional within its function and can escape one region, because the caller supplied it.
- 3Losing regional informationPassing a regional function through an identity function returns a local value that cannot be passed to the tail call.
- 4Regions around loopsEach loop-body region can reclaim local allocations before the next iteration.
ポイント
- A parameter marked local is treated as regional inside the function.
- A regional value may cross one region boundary because it originated outside that region.
- Returning such a value exposes it as local to the caller rather than preserving regional information.
- Passing a regional value through a local-returning identity function can lose the information needed for a tail call.
- Loop bodies create regions that allow local allocations to be reclaimed on each iteration.
チャプターごとに整えた全文を読む
Two surprising examples
A local identity function returns its input, while a recursive iterator passes a local function into a tail call. Both appear to conflict with the region rules.
This episode continues the series on local values in Jane Street’s publicly available OCaml compiler branch. I want to explain regions and a hidden mode called regional, using examples that initially appear to violate the rules from earlier videos.
Consider an identity function with a parameter annotated as a local string. It compiles, and the inferred type takes a local string and returns a local string. That seems surprising: every function defines a region, and a local value is not supposed to escape its region.

A second example is a recursive iterator over a list. It accepts a function, applies it to each head and makes a tail-recursive call on the remainder. Marking the function parameter local permits a caller to provide a closure without necessarily allocating that closure on the heap.

The iterator still compiles even though the recursive call is a tail call, which lies outside the current function’s region. Why can the function parameter be passed there? The identity function’s return and the iterator’s tail call both seem to allow local values to escape.
The regional mode
Local parameters are treated as regional within a function and can cross one region boundary. Passing one through a local-returning function loses that information.
The explanation is that a parameter marked local is treated as regional inside the function. Regional is a hidden mode, not a keyword that can be written in the program. A regional value may escape one region, though it does not have to escape at all.

A parameter must have been supplied by a caller, so it already lives beyond the callee’s own region. Letting it cross that one boundary is safe. In the identity example, the returned string is local to the caller: it cannot simply become global, which would allow an actually local value to escape too far.
In the iterator, the function argument is also regional within the current invocation. It can therefore cross the boundary at the tail call and be passed to the next invocation, even though the current function’s region ends before that call.
To show the distinction, I pass the iterator’s function argument through an identity function whose type takes and returns a local value. Its result is known only as local; the type does not preserve the fact that the original argument was regional. Passing that result to the tail call now produces an escaping-local error.

Regions in loop bodies
The presenter rewrites the iterator as an array loop. Its per-iteration region determines when local allocations can be reclaimed.
Regions also surround loop bodies. I rewrite the iterator to traverse an array with a for loop and call the function for each element. The example is about the lifetime of local allocations made while the loop runs.
If the called function returns locally, its local allocations are made in its parent’s region—in this case, the loop-body region. Each time an iteration ends, that region can be reclaimed before the next iteration. Without such a boundary, repeated work could accumulate space across the loop.

The presenter notes that the loop rule is less consequential for typing in this example because loops do not normally return a value except via mechanisms such as references. It is still useful for understanding allocation behavior: local allocations in a loop’s region end with the iteration.
同じチャンネルの記事
15:33EN
1:48:29EN
1:36:37EN
1:05:45EN
1:25:00EN
1:16:29EN