30초 요약
In the second part of an Effective ML lecture, the speaker argues that readable, robust software makes failure explicit and abstracts repeated patterns without introducing unnecessary complexity. He also discusses mutation, code review, team size, human involvement in trading and the historical choice of OCaml.
- 1Options and exceptionsA search can return an option or provide an explicitly named exception-raising variant, depending on caller needs.
- 2Boilerplate hides changeRepeated code is hard to update consistently and tedious to review.
- 3Complexity is the enemyA sophisticated type-level invariant is not helpful if it makes the interface incomprehensible.
- 4Mutation as communicationShared mutable state links parts of a program and can make local reasoning harder.
- 5Reviewing critical codeThe speaker describes multiple reviewers and tests for the trading system’s critical path.
핵심 정리
- Expose expected failure in types when callers need to handle it, and name exception-raising alternatives clearly.
- Abstract repeated patterns so common behavior and meaningful variations are visible.
- More static guarantees are not automatically better if they make interfaces too complex to understand.
- Avoid unnecessary mutation while recognizing that useful programs need side effects.
- Code review, tests and small coherent projects support dependable systems.
- Language choices depend on the performance and operational needs of the application.
챕터별로 다듬은 전문 읽기
Make errors explicit
The speaker contrasts option-returning interfaces and exceptions. He explains why error-handling choices depend on the consequences of failure.
A key idea in effective ML programming is to make common errors in called functions clear and explicit. The familiar advice to use exceptions for exceptional circumstances reflects two concerns: exceptions create a nonlocal control flow, and implicit error handling makes it easy not to think about failure.
I do not argue against exceptions altogether; they are useful. But whether a failure is exceptional depends on the caller’s context. A list search might be expected always to find an element in one piece of code, while another caller genuinely does not know whether it exists.
A list interface can offer both find, which returns an option, and find_exn, which may raise an exception. The former makes absence visible in the type and forces the caller to handle it; the latter makes the possible exception visible in its name. Our convention uses the _exn suffix for operations that can commonly fail on their inputs.
Usually the option-returning version is preferable because the type system carries the possibility of failure rather than relying solely on a naming convention. We learned this through experience and revised much of our code in this style to improve robustness.
Context matters to error-handling design. Stephen Weeks, author of the MLton compiler, observed a change in perspective when moving to Jane Street: a compiler that exits with an exception and stack trace is inconvenient, but a trading system that dies in the middle of the day is a much more serious problem.
Some interfaces avoid the problem entirely. A fold can operate on an empty list, unlike an operation that requires a head or a successful search. A function with simple error behavior is easier to use confidently, while explicit failure conditions make the remaining cases easier to reason about.
Avoid boilerplate
Repeated patterns can hide mistakes and become hard to update. ML abstractions can make the common pattern and its variations explicit.
Next, avoid boilerplate: similar code copied repeatedly with small but important changes. Even good programmers use it when the language does not provide an easy abstraction for the pattern, or when they do not know how to express that abstraction.
Repetition creates two problems. A pattern evolves, and developers may miss one of its many copies when changing it. Repetitive code is also dull to review; people examine the first instance carefully and can overlook a significant difference in later copies.
C provides relatively limited abstraction tools for this kind of pattern, leaving macros or copying as common choices. ML offers functors and higher-order functions. I suggest a low tolerance for duplication: even two copies are a reason to look for an abstraction.
An abstraction can be worthwhile even when it makes the code longer. A local function states the common pattern once, and its arguments show what varies. The resulting code can be more explicit and easier to follow than several nearly identical copies.
Control complexity
Type-level guarantees and purity are valuable, but excessive sophistication can make code harder to understand. The goal is a practical balance.
Powerful type systems create the opposite temptation: encoding increasingly intricate invariants because it is fun and possible. I have seen interfaces that experienced ML programmers cannot understand even after explanation. At that point the extra static guarantee may have increased overall complexity rather than helping.
The central challenge in software engineering is managing complexity. Learn clever techniques and use them where they help, but exercise restraint. Code must fit the minds of the people who will maintain it, not merely an elegant abstract model.
Side effects and mutation deserve similar balance. Prefer code without unnecessary mutation because a shared reference is a communication channel between different parts of a program. Such channels connect distant code and make it harder to understand a component in isolation.
Do not turn purity into a religion. Useful programs interact with the outside world: reading a socket, writing a file or checking the clock is a side effect. Haskell disciplines the separation between pure and effectful code; it does not eliminate effects from useful programs.
Pushing effects outward can help, but not if the result becomes more complicated than a straightforward imperative solution. Sometimes an imperative algorithm is easier to understand or reaches good performance without elaborate machinery. The aim is the simplest system that serves the task.
Questions on engineering and trading
The discussion covers review, small teams and the role of people in trading systems. The speaker closes with the historical choice of OCaml.
Asked about review, I explain that Jane Street treats code between a trading decision and the market as a critical path. Multiple people—at least two, usually three—are assigned to read changes before production, and an internal system tracks that review. Unit and regression tests also matter, but review is central to keeping the systems safe and coherent.
We try to organize development into coherent projects with small groups, roughly one to six people at the outside. Small, relatively independent pieces can fit in individuals’ heads and fit together into a larger system. I would find a large group working directly on one piece of infrastructure difficult to manage.
Asked about automation, I say the business varies widely. Some work involves people negotiating by phone, while other systems run for hours with humans watching rather than continually changing them. Most systems involve substantial human interaction; the firm is not simply a machine that runs unattended and produces money.
Trading is a game involving someone on the other side of every transaction. A model of the market must account for that other participant. Human attention, from both traders and technologists, is a central part of the integrated human and technical system.
Jane Street had used OCaml for about nine years at the time of this talk, after earlier systems using Excel, Java and C#. The choice was partly contingent on my familiarity with the language. OCaml also offered a useful balance: declarative code with a comparatively straightforward, efficient compilation strategy.
One reason we did not choose Haskell was that its performance, especially space usage, was harder to reason about for our needs at the time. Its implementation has improved, but the effort needed to make it efficient adds complexity. Language choice depends on the requirements of the system being built.
같은 채널의 다른 글
15:33EN
1:48:29EN
1:36:37EN
1:05:45EN
1:25:00EN
1:16:29EN