Programming with OCaml's Local Mode

An example shows how a local list can contain global values, why a non-escaping annotation alone does not ensure stack allocation, and where the experimental feature remains awkward.

채널
Jane Street
게시일
2024년 6월 24일
영상
14:36
읽기
3분
슬라이드
5
원문
영어
YouTube에서 열기
스크롤하면 영상이 오른쪽 아래로 작아져 계속 재생됩니다. 자막은 읽기 언어를 따릅니다.
읽기 언어2개 언어로 읽을 수 있음
이 글은 아직 한국어 번역이 없어 영어 원문으로 표시합니다.
요약

30초 요약

A Jane Street speaker demonstrates local mode in an experimental OCaml compiler branch. Building a temporary list of widgets exposes tail-call restrictions, global modality, wrapper types and the need for a local-allocating map.

  1. 1A list that must not escapeThe intermediate widget list can be passed to get_best only when the function promises not to retain it.
  2. 2Tail-call restrictionThe local argument cannot be used in that tail call, so the example opts out of tail-call optimization.
  3. 3Global elements inside a local listA globally modal field in an unboxed wrapper preserves the elements' global status even when the list is local.
  4. 4Local does not mean stack allocatedOrdinary List.map still allocates on the heap even if its result is treated as local.
  5. 5Allocate in the caller's regionA local-producing map uses exclave in tail position to allocate its result in the caller's region.

핵심 정리

  1. A local parameter promises that its argument will not escape, which may require avoiding a tail call.
  2. A local list can contain global elements using a wrapper field with global modality.
  3. An unboxed single-field wrapper can express this distinction without adding a runtime wrapper allocation.
  4. A local annotation expresses non-escape; it does not by itself ensure that a value is allocated on the stack.
  5. A local-producing map can allocate list cells in the caller's region using exclave.
  6. Mode polymorphism and more convenient modality syntax were future possibilities, not completed features in this demonstration.
전문

챕터별로 다듬은 전문 읽기

원본 자막만 보기
01

A temporary widget list

The speaker introduces the example and tries to make its intermediate list local. A local parameter and a nontail call resolve initial escape errors.

In this final installment of the current series on locals, I want to work through a small programming example using the experimental feature in Jane Street's OCaml compiler branch. The example is deliberately contrived, but the errors we encounter illustrate practical issues that can arise when working with local values.

Imagine a map of widgets indexed by integers. We want the best widget associated with a prime below 100. A list of primes is already available, and a get_best function takes a widget list and returns the best element. For the demonstration, I supply placeholder helpers that compile but are not intended to run.

The initial implementation maps over the primes, looks up each widget, collects the results in a list, and passes that list to get_best. It compiles. But the intermediate list is only needed during this function, so I would prefer not to allocate it on the heap.

The temporary widget list
슬라이드 1 / 5The temporary widget listThe initial function maps prime keys to widgets and passes the list to get_best.

Annotating the list as local produces an escape error: the original get_best signature expects a global list and makes no promise that it will not store the argument. Marking its list parameter local gives the compiler the needed non-escape promise.

That exposes another error: get_best is in tail position, so the local argument cannot be passed in that tail call. Adding a nontail annotation keeps the relevant region alive. In this nonrecursive example, sacrificing tail-call optimization is acceptable.

A local argument in a tail call
슬라이드 2 / 5A local argument in a tail callThe compiler reports that the local value escapes because the call is in tail position.
02

Global elements and local structure

Returning an element from a local list reveals a locality problem. A globally modal field in an unboxed wrapper preserves global elements.

The placeholder signature now accepts a local list and returns a global widget, but we have not implemented it. A real implementation that takes the first element reveals the next problem: a value extracted from a local list is treated as local, so returning it globally would let it escape.

After I mark get_best's input local, its inferred result is also local. The caller wants to return a global widget from the original table, however. The distinction we need is a list whose cells are local but whose elements are global.

The solution demonstrated is a wrapper type with a global modality on its single field. The wrapper is marked unboxed, a general OCaml feature for this kind of single-field record: the wrapped value and wrapper have the same runtime representation, so the wrapper itself does not require an extra allocation.

The global modality says that the field remains global even when the enclosing wrapper is local. A local list of these wrappers can therefore contain global elements. Pattern matching extracts the field, allowing get_best to return it globally without letting the local list itself escape.

A globally modal wrapper
슬라이드 3 / 5A globally modal wrapperAn unboxed single-field record lets a local list hold values known to be global.
03

Non-escape versus allocation

Wrapping values restores the desired interface, but a local annotation alone does not change how List.map allocates. A local-producing map uses exclave.

The inferred input type initially expects a map of wrapped widgets, which is not what we started with. Wrapping each widget after the lookup fixes the interface: the input remains a map of ordinary widgets, the result is global, and the intermediate list is local.

Wrapping the widget lookup
슬라이드 4 / 5Wrapping the widget lookupThe map wraps each retrieved widget so the public input remains a map of ordinary widgets.

There is still a subtlety. A local annotation says the list does not escape; it does not by itself guarantee stack allocation. Ordinary List.map allocates its result on the heap, and a global value can be treated as local. The code may type-check while failing to achieve the desired allocation behavior.

To actually allocate the intermediate list locally, I write a separate recursive map. In a full Jane Street environment, library functions can provide this functionality, but the example uses the standard library context and defines its own version.

The map's type must say its result is local. Its cons cells need to be allocated in the caller's region, so the implementation uses exclave in a tail position. With that change, the map returns a locally allocated list and the caller can infer its locality without an explicit annotation.

A local-producing map
슬라이드 5 / 5A local-producing mapThe custom map uses exclave to construct its result in the caller’s region.
04

Practical rough edges

Wrapping elements and maintaining a separate local map introduce verbosity. The speaker discusses possible future improvements while stressing that the feature is experimental.

The example exposes two sharp corners: preserving global elements in a local list requires wrapping and unpacking them, and ordinary map needs a local-producing counterpart to control allocation. The team hopes mode polymorphism may improve this and is exploring possible syntax for inline modalities, but the work remains experimental.

같은 채널의 다른 글

1:48:29EN
JSJane Street· 2026년 10월 16일How AI Is Changing Programming: John Krazit and Ron MinskyRon Minsky, John Krazit · Jane StreetJane Street’s AI tooling lead and the Signals and Threads host examine agent infrastructure, security, code review and the changing experience of writing software.코딩 에이전트Developer productivity평가(Evals)+2
1:36:37EN
JSJane Street· 2026년 9월 23일Aaron Bauer on Teaching Software Engineering in the Age of AIRon Minsky, Aaron Bauer · Jane StreetHow learning goals, active practice and editor design shape engineering education when models can generate the code.AI in EducationSoftware engineeringEducation+2
슬라이드 9장1:05:45EN
JSJane Street· 2026년 9월 11일Cyrus Omar on Live Programming and a Computational Commons for Humans and AICyrus OmarA demonstration of Hazel shows how immediate feedback, structured collaboration and carefully scoped permissions could support people and AI agents working together.Programming LanguagesHuman-AI Collaboration개발 워크플로와 테스트+2
1:25:00EN
JSJane Street· 2026년 9월 2일Eric Mannes on Turning Messy External Data Into Trading ResearchRon, Eric Mannes · Jane StreetFrom commodity futures and an Excel risk system to point-in-time datasets and AI, Eric Mannes explains the domain knowledge behind data engineering at Jane Street.Data quality & curation개발 워크플로와 테스트Investing+2
슬라이드 7장1:16:29EN
JSJane Street· 2026년 8월 26일Grant Sanderson and Alok Puranik: Positional Encodings Through Group TheoryGrant Sanderson, Alok Puranik · 3Blue1BrownStarting with assumptions about attention, they derive matrix exponentials and connect rotations, decay and Jordan blocks to RoPE and ALiBi.Transformers & attentionMathematics머신러닝 기초+1
27:39EN
JSJane Street· 2026년 8월 21일3Blue1Brown and Jane Street on Trading, Machine Learning and CultureGrant Sanderson, Nitya, In Young, Craig, Alok · 3Blue1BrownGrant Sanderson talks with Jane Street colleagues about market making, shared compute, research and the paths that brought them to the firm.Machine LearningFintechCompany Culture+2