Why Functional Programming Matters at Jane Street

A Jane Street developer revisits John Hughes’s classic argument and explains why expressive static types rank highest for trading systems.

Kanal
Jane Street
Veröffentlicht
9. März 2017
Video
25:44
Lesezeit
7 Min.
Folien
7
Original
Englisch
Auf YouTube öffnen
Beim Scrollen schrumpft das Video in die rechte untere Ecke und läuft weiter. Untertitel folgen Ihrer Lesesprache.
LesespracheIn 2 Sprachen verfügbar
Diesen Artikel gibt es noch nicht auf Deutsch, daher wird das Original (Englisch) angezeigt.
ZUSAMMENFASSUNG

In 30 Sekunden

A Jane Street developer compares the practical value of higher-order functions, purity and laziness with expressive static types. Using trading messages, map merging and connection states, the speaker shows how types can make code clearer and help prevent invalid states.

  1. 1Hughes’s three featuresThe speaker revisits higher-order functions, purity and laziness as tools for modularity and managing complexity.
  2. 2Trading-system prioritiesCorrectness, clarity, dexterity and performance matter when small errors can be costly and opportunities or regulations change quickly.
  3. 3Expressive static types come firstThe speaker ranks types above higher-order functions, purity and laziness for Jane Street’s work.
  4. 4Let the compiler find missing casesAdding a bust to an exchange-message variant exposes position-handling code that must be updated.
  5. 5Make illegal states unrepresentableA Left/Right/Both type eliminates an impossible map-merge case, and connection variants encode which data belongs to each state.
  6. 6Laziness and predictabilityThe speaker values laziness in some cases but says its performance can be difficult to predict in volatile markets.

Das Wichtigste

  1. Higher-order functions allow programmers to define control flow and compose behaviour.
  2. Pure functions are easier to reason about, but real systems must accommodate side effects.
  3. Trading systems need correctness, clarity, fast safe changes and predictable performance.
  4. Option and variant types make possible cases explicit and let the compiler expose omissions.
  5. Designing types so illegal states cannot be represented reduces the burden of maintaining invariants.
  6. Laziness can separate concerns but may make performance harder to predict.
VOLLTEXT

Den bearbeiteten Text nach Kapiteln lesen

Nur das Transkript
01

Revisiting functional programming

The speaker responds to John Hughes’s paper and introduces its three features. The talk asks which features matter most in practice.

I’m a Jane Street developer and spend most of my day writing functional code in OCaml. The title is a trick: I like functional programming, but I want to explain both why it matters and why some of its familiar justifications matter less to us.

This talk responds to John Hughes’s 1988 paper, Why Functional Programming Matters. Hughes makes a compelling case for functional programming, and I recommend reading it. I agree that it is valuable, but I would put different reasons at the top of the list.

Hughes identifies higher-order functions, purity and laziness as characteristic strengths. Software engineering is largely about managing complexity by separating concerns and composing modules; he presents these features as glue that makes modularity possible.

02

Higher-order functions and purity

First-class functions enable custom control flow. Purity makes composition and reasoning easier, though real systems require side effects.

A higher-order function treats functions as first-class values: they can be stored in a hash table, passed to another function or put in a reference. An OCaml map function, for example, takes a list and a function, applies the function to each head and recursively maps the tail.

A higher-order map in OCaml
FOLIE 1 / 7A higher-order map in OCamlThe recursive function takes another function and applies it to every list element.

Higher-order functions let us define our own control flow instead of relying only on mechanisms provided by the language designer, such as loops, iterators or comprehensions. A large function might initially select behaviour with a Boolean flag, then a variant; as choices multiply, passing in a function lets it hand off control and resume when that function returns.

Purity means a function’s behaviour depends only on its inputs and its only effect is to return its result. It does not depend on a counter or the time of day, write a file or print to the screen. Pure functions are easier to reason about and compose much like mathematical functions.

Mutation can create a communication channel between parts of a program through shared state. Because that channel is not apparent in a function’s inputs and return value, it can make code hard to reason about. Nevertheless, real systems need side effects, such as communicating with an exchange, so enforcing purity at every scope can be difficult.

03

Laziness and modularity

Lazy evaluation enables infinite structures and separates the chess search from generation of possible moves. Jane Street uses laziness less often.

Laziness means an expression is evaluated only when needed. An infinite list of natural numbers can be defined by putting n before the numbers starting at n + 1: a strict evaluation would run forever, while a lazy language such as Haskell can consume the list as needed.

Hughes uses a chess example to show how laziness separates termination conditions from loops. One part of a program can define a tree of possible moves from the rules of chess, while another traverses it to find an optimal move. The traversal can treat the tree as available without generating the whole thing in advance; without laziness, it must also manage generating the nodes it needs.

We agree that higher-order functions are extremely useful. Purity helps to a point but can make large systems harder to write, and Jane Street reaches for laziness relatively rarely. Our priorities reflect the systems we build.

04

What trading systems need

The firm’s systems must be correct and clear, easy to change safely, and reasonably performant. Predictability matters alongside speed.

Jane Street is a proprietary trading firm and market maker, trading its own money on exchanges around the world. At the time of this talk, we make roughly a million trades a day across about 200 exchanges and have about a petabyte of historical data. Our systems are written in OCaml.

We care first about correctness. Our systems handle billions of dollars, and a small bug in a hot loop can become very expensive in seconds. Because we trade our own money, a serious loss directly affects the firm. Clarity matters too: code that happens to be correct but is so obscure that nobody can establish why is almost as troubling as incorrect code.

We also value dexterity: the ability to change systems quickly, efficiently and safely. Trading opportunities can disappear quickly, and regulatory changes may come with deadlines after which we cannot trade. With many systems per developer, we want to avoid boilerplate and have language support that helps prevent unintended consequences.

Performance matters as well, though we are not primarily trying to win a race to minimise exchange latency. Trading code must be competitive, and analyses over a petabyte of data cannot take forever. OCaml is not as fast as C, but it offers reasonable and relatively predictable performance.

The relative simplicity of OCaml’s compiler helps us understand what code executes at the machine level; small source changes tend not to cause large performance changes. By contrast, I have seen subtle changes in Haskell prevent a compiler optimisation and produce a performance change that might not be apparent in review. This is a trade-off, not a reason to reject functional languages on performance grounds.

05

Static types and null safety

Expressive static types lead the speaker’s list. Option types make the absence of a value explicit and require it to be handled.

Our ranking puts expressive static types first, higher-order functions next, then purity, with laziness much lower. An expressive type system can represent tuples, records and algebraic data types; static checking lets the compiler check their safe use before the program runs.

Consider null pointers. A programmer can overlook a case where a value is null and trigger an exception. Tony Hoare called the invention of the null reference his billion-dollar mistake. In OCaml, a value of type t is a t, rather than a t that may silently be null; an option type explicitly represents Some value or None.

When an option is used, the compiler requires us to consider both possibilities. In languages without such expressive types, a comment might promise that a function never returns null, but a comment must be trusted and maintained. A type signature makes the possibility visible and lets the compiler enforce handling it.

06

Extending exchange messages

An exchange-message variant models acknowledgements, outs and fills. Adding a bust lets the compiler flag code that needs a new case.

Types also help us extend systems safely. Imagine an execution message from an exchange: an acknowledgement says an order message was received, an out says an order is no longer live, and a fill reports a trade with a size and direction. A position-update function matches these cases: acknowledgements and outs leave the position unchanged, while fills change it.

Exchange messages and position updates
FOLIE 2 / 7Exchange messages and position updatesThe variant describes acknowledgements, outs and fills, while the match updates positions.

Now suppose the exchange also sends a bust, cancelling a previously reported fill. These messages occur regularly enough that we must handle them. When we add a bust to the variant type, the compiler identifies matches that have not considered the new case, including code elsewhere in the system. I use this technique when adding features to find all the places whose behaviour needs reconsideration.

Adding a bust to the message type
FOLIE 3 / 7Adding a bust to the message typeA new variant represents the cancellation of an earlier fill.
07

Making illegal states unrepresentable

Map merging and connection state examples show how types eliminate impossible combinations. This makes invariants clearer and easier to maintain.

A map merge signature
FOLIE 4 / 7A map merge signatureThe combining function receives optional values from the two input maps.

A second example is merging two maps. Both have the same key type, and a supplied function receives an optional value from each map for a key and returns an optional result. The merge considers keys present in either input and builds the resulting map from the function’s results.

A caller matching the two optional values appears to have four cases, but the case where both are None cannot occur: the key must have appeared in at least one input map. One could assert that this case is impossible, but a reader or reviewer still has to verify the claim.

A type for one or both values
FOLIE 5 / 7A type for one or both valuesLeft, Right and Both remove the impossible case in which neither input contains the key.

Instead, define a type with Left, Right and Both cases and pass that to the combining function. The impossible None/None case disappears. The code becomes clearer, reviewers no longer have to prove that branch unreachable, and we prune an unnecessary branch of the combinatorial space early.

The larger principle is to make illegal states unrepresentable. If certain combinations of values cannot coexist, encode that fact in the types. You can maintain an invariant by hand, arguing that every function preserves it, but that is hard to sustain; a suitable type can enforce it by construction.

Connection state with optional fields
FOLIE 6 / 7Connection state with optional fieldsThe initial record allows combinations of fields that may not make sense for its state.

For example, a connection record might contain a state, server, ping information and timestamps. What does a disconnection time mean when the connection is currently connected? What does a last ping mean if it is disconnected or has never received one? Not every field is meaningful in every state.

Instead, use variants that explicitly carry the information available in each state. The disconnected case contains a disconnection time that the other cases do not. This makes the invariant visible in the type and prevents accidental invalid combinations, improving both correctness and clarity.

Connection state as variants
FOLIE 7 / 7Connection state as variantsEach state carries only the information relevant to it.
08

The final ranking

The speaker explains why lazy evaluation can complicate performance and purity can add complexity. Expressive static types and higher-order functions are the highest priorities.

To recap, laziness can be useful but makes performance difficult to reason about. A function that has behaved predictably many times may suddenly be much slower in an unfamiliar, volatile market situation—precisely when the system needs to stay available for trading.

Purity is useful, and OCaml supports it, but if enforcing it introduces more complexity, the trade is not worthwhile for us. Higher-order functions are excellent, while expressive static types sit at the top of our list. Fortunately, languages such as OCaml and Haskell provide these features together; the ranking describes what we would choose if we had to choose.

Mehr aus diesem Kanal

6 Folien15:33EN
JSJane Street· 26. Okt. 2026Figgie Strategy: Reading Hands, Winning Majorities and Pricing CardsBradley, Mike · Jane StreetJane Street traders Bradley and Mike analyze three games to show how private information, market competition and timing affect card prices.Decision MakingMathematicsFintech
1:48:29EN
JSJane Street· 16. Okt. 2026How 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.Coding-AgentenDeveloper productivityEvals+2
1:36:37EN
JSJane Street· 23. Sept. 2026Aaron 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 Folien1:05:45EN
JSJane Street· 11. Sept. 2026Cyrus 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 CollaborationEntwicklungs-Workflows & Tests+2
1:25:00EN
JSJane Street· 2. Sept. 2026Eric 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 & curationEntwicklungs-Workflows & TestsInvesting+2
7 Folien1:16:29EN
JSJane Street· 26. Aug. 2026Grant 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 & attentionMathematicsML-Grundlagen+1