anilmvs
Standard Chartered disabled default recursion and replaced lazy evaluation with strict execution in its proprietary compiler because unpredictable memory blowups and untraceable runtime crashes make standard Haskell unworkable for enterprise production.
AI-assisted summary of CUFP 2011 Keynote: Pragmatic Haskell (Lennart Augustsson Standard Chartered Bank) — original source. Check the original for full context.
Source published:
Key ideas
- Lennart Augustsson details Mu, a proprietary Haskell dialect at Standard Chartered Bank that runs 400,000 lines of production code across 2,400 modules, replacing laziness with strict evaluation and implementing strings as compact abstract data types rather than linked lists of characters. (11:59)
- General recursion is disabled by default in Mu, requiring the explicit language pragma {-# LANGUAGE Recursion #-}; an internal audit revealed that only roughly 6% of production modules ever activate recursion, as quants rely instead on bulk array combinators and relational operations. (14:31)
- Standard Chartered integrates Mu deeply into Microsoft Excel by wrapping complex functional data structures into tagged handle strings, exposing sheet-defined functions to higher-order combinators, and providing runtime native JIT compilation via LLVM IR. (29:47)
- Augustsson argues that while laziness provides theoretical elegance and semantic compositionality, strictness delivers critical operational benefits in enterprise software, notably predictable resource consumption and deterministic chronological stack traces on failure. (50:22)
Detailed notes
## Background This keynote was recorded at CUFP 2011 and published on YouTube by anilmvs on March 27, 2012. According to the description, functional programming pioneer Lennart Augustsson reflects on his commercial use of Haskell and his creation of four Haskell implementations—three of which are unorthodox—while demonstrating his strict internal dialect used at Standard Chartered Bank and providing a pragmatic assessment of strict versus lazy evaluation.
## TL;DR Augustsson chronicles his history building non-standard Haskell compilers—from the early days of HBC and pH to Bluespec and his strict proprietary dialect, Mu, built for Standard Chartered Bank. He explains how Mu departs from standard Haskell by enforcing strict evaluation, disabling general recursion by default, abstracting strings, and splitting effects into a two-tier IO model tailored for financial models. Through live demos, he shows how Mu compiles Haskell and sheet-defined functions directly inside Microsoft Excel, serializing intermediate representation for distributed compute farms and LLVM JIT compilation. Finally, he weighs the trade-offs of strict versus lazy semantics in production, arguing that strictness provides vital stack traces, predictable resource compositionality, and clear compiler diagnostics across a 400,000-line codebase.
## Atmosphere & style Augustsson delivers the address with an unassuming, dryly humorous, and thoroughly unpretentious tone, effortlessly deflating theoretical dogma with candid lessons from commercial engineering. The atmosphere is casual yet intellectually engaging, blending witty anecdotes about early Haskell history with wry commentary on corporate banking habits and Excel's interface quirks. Watching the session feels like listening to an experienced compiler veteran demystify complex language internals through an accessible, pragmatic lens.
## Bulletin - [11:59](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=719s) Lennart Augustsson details Mu, a proprietary Haskell dialect at Standard Chartered Bank that runs 400,000 lines of production code across 2,400 modules, replacing laziness with strict evaluation and implementing strings as compact abstract data types rather than linked lists of characters. - [14:31](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=871s) General recursion is disabled by default in Mu, requiring the explicit language pragma {-# LANGUAGE Recursion #-}; an internal audit revealed that only roughly 6% of production modules ever activate recursion, as quants rely instead on bulk array combinators and relational operations. - [29:47](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1787s) Standard Chartered integrates Mu deeply into Microsoft Excel by wrapping complex functional data structures into tagged handle strings, exposing sheet-defined functions to higher-order combinators, and providing runtime native JIT compilation via LLVM IR. - [50:22](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3022s) Augustsson argues that while laziness provides theoretical elegance and semantic compositionality, strictness delivers critical operational benefits in enterprise software, notably predictable resource consumption and deterministic chronological stack traces on failure.
## Key takeaways - [03:04](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=184s) Lennart Augustsson authored HBC in 1990 as the first public Haskell compiler using native code generators across multiple architectures, outperforming GHC on several benchmarks as late as 2007. - [05:49](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=349s) MIT's pH (Parallel Haskell) operated as an eager, non-strict functional language using speculative lightweight threads and runtime thread termination, directly prompting the Haskell committee to adopt the term 'non-strict' over 'lazy'. - [08:25](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=505s) Bluespec originally used pure Haskell as a compile-time elaboration stage to synthesize hardware with type-level natural arithmetic, but pivoted to SystemVerilog syntax due to commercial resistance to Haskell syntax among hardware engineers. - [11:59](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=719s) Mu diverges significantly from standard Haskell by adopting strict-by-default evaluation, abstract string representations instead of [Char], and omitting arbitrary-precision Integer by default, which prevents direct adoption of Hackage packages. - [14:31](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=871s) To mirror Microsoft Excel's distinction between pure cell formulas, volatile read calculations, and mutating macros, Mu separates side effects into a read-only SafeIO monad and standard IO. - [17:31](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1051s) Lists in Mu are internally backed by flat arrays, delivering O(1) indexed random access but altering the operational cost model for prepending and concatenation compared to linked lists. - [19:57](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1197s) The Mu compiler assumes that all input programs terminate, allowing it to freely apply both strict and lazy compiler optimizations, including dead-code elimination of unused bindings containing trace side effects. - [22:47](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1367s) Standard Chartered's Cortex platform enables cross-language interoperability across C++, compiled Haskell, Mu, C#, Java, and Excel via uniform binary serialization and calling conventions. - [29:47](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1787s) Unlike competing investment banks that map pointers directly to floating-point numbers or negative integers, Standard Chartered safely represents complex object handles inside Excel's four-type system using tagged string identifiers. - [37:50](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=2270s) Excel users can turn ordinary grid formulas spanning input and output cells into first-class sheet-defined functions that can be passed directly into higher-order functional combinators like map. - [42:46](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=2566s) Mu exposes native JIT compilation under explicit user control via LLVM, performing runtime partial evaluation, constant propagation, and dead-branch elimination on partially applied functions. - [46:54](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=2814s) All Mu functions retain their intermediate representation throughout their lifecycle, enabling distributed cross-architecture execution where platform-neutral IR is serialized and compiled natively on remote compute nodes. - [50:22](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3022s) Strict evaluation delivers superior resource compositionality and immediate, chronological call-stack traces upon runtime errors, resolving two of the biggest operational friction points of lazy Haskell. - [53:36](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3216s) Mu implements non-strict control-flow primitives such as when, unless, and fromMaybe using a by-name keyword that uses hygienic let-binding expansion to avoid repeated evaluations of shared arguments. - [58:27](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3507s) Mu improves developer productivity for non-CS quants by providing two-location type error diagnostics that pinpoint both conflicting type sources simultaneously, rather than reporting an arbitrary traversal unification failure. - [58:27](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3507s) The entire 400,000-line Mu codebase type-checks in roughly 30 seconds on a developer desktop, and adoption has flourished organically among front-office traders using Mu as a type-safe glue layer over C++ numerical routines.
## Detailed walkthrough ### [00:09](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=9s) Introduction to CUFP 2011 and Lennart Augustsson
The Commercial Users of Functional Programming (CUFP) 2011 conference opens with introductory remarks reflecting on the accelerating momentum of functional programming across industry. More developers and companies than ever are discovering functional languages as pragmatic "secret weapons" to solve complex real-world problems across diverse domains. To kick off the keynote, the host introduces Lennart Augustsson of Standard Chartered Bank, describing him as a legendary pioneer who has been at the center of functional language design and compilation since the very beginning, including his role in building the early compilers for lazy functional languages and serving as one of the founding figures of the Haskell language committee.
Taking the stage to applause, Augustsson sets a relaxed, unassuming tone for his talk, titled *Pragmatic Haskell*. He announces that rather than offering an overly theoretical academic presentation, he will explore the practical realities of using functional programming in production, walk through several idiosyncratic and unorthodox Haskell implementations he has created over his career, give a demonstration of the functional tools used at Standard Chartered Bank, and present a pragmatic critique of strict versus lazy evaluation. As Lennart Augustsson outlines his agenda: “So this is not going to be a deep talk; I'm going to talk about a couple of different things.”
```mermaid flowchart LR accTitle: Evolution of Lennart Augustsson's Haskell Implementations accDescr: Diagram tracing the four non-standard Haskell implementations developed by Lennart Augustsson from HBC in 1990 to Mu at Standard Chartered. subgraph Early["Academic & Parallel"] HBC["HBC (1990)<br/>First public Haskell compiler<br/>Lazy evaluation & native code"] PH["pH (ca. 1995)<br/>Parallel Haskell at MIT<br/>Eager non-strict & MVars/IVars"] end subgraph HardwareAndIndustry["Hardware & Finance"] BS["Bluespec (ca. 2000)<br/>Two-level hardware synthesis<br/>Type-level numeric arithmetic"] MU["Mu (Standard Chartered)<br/>Strict by default & no recursion<br/>SafeIO & array combinators"] end HBC --> PH PH --> BS BS --> MU ```
[03:04](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=184s) HBC: The First Public Haskell Compiler
Augustsson clarifies a misconception about his role at the dawn of Haskell. While he did build HBC (the Chalmers Haskell Compiler), the very first publicly available Haskell compiler, he was not present at the initial formative meeting in Portland, Oregon in 1988 where the committee was formed. Instead, his Chalmers colleague Thomas Johnsson attended. As Augustsson tells the story, when it came time to implement the language, he and Johnsson literally flipped a coin; Johnsson won the toss and chose to remain working on the Lazy ML (LML) compiler, leaving Augustsson with the task of writing HBC.
Released in 1990, HBC saw its most recent release in 2004—a distinction Augustsson jokingly emphasizes. Even years later, the compiler showed surprising endurance: in 2007, when Don Stewart ran Haskell benchmark suites, HBC still managed to beat the Glasgow Haskell Compiler (GHC) on several tests. However, maintaining HBC was grueling because Augustsson had built native code generators targeting an excessive number of machine architectures, ranging across SPARC, VAX, m68k, Alpha, HP-PA, and x86. Shipping a release meant cross-compiling and testing across obsolete systems, making updates painful. HBC stuck closely to the emerging Haskell standard with minimal extensions, though it did pioneer early support for existential types.
- HBC was first released in 1990, with a final maintenance release in 2004. - In 2007 performance benchmarks, HBC still outperformed GHC on several benchmark programs. - As Augustsson wryly quipped about HBC's timeline: “I said first released in 1990; I say latest release in 2004, I didn't say last.” - The compiler avoided C as an intermediate layer, relying instead on handcrafted native code backends for far too many architectures.
[05:49](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=349s) pH: Parallel Non-Strict Haskell at MIT
Augustsson later set out to rewrite the Haskell compiler entirely in Haskell. While he never completed the original project, his front-end codebase became the foundation for pH (Parallel Haskell), developed at MIT under Professor Arvind and Rishiyur S. Nikhil. pH took an entirely different approach to executing functional code: it discarded lazy evaluation in favor of fine-grained eager parallel evaluation while preserving non-strict semantics. Augustsson explains that the Haskell report explicitly uses the phrasing "a non-strict functional language" rather than "lazy" specifically because Arvind objected to the term during committee meetings, having pH's execution model in mind.
In pH, expressions were evaluated speculatively and ahead of time in independent lightweight threads rather than waiting for values to be demanded. If a computation turned out to be unneeded, an aggressive runtime scheduler was responsible for detecting that fact and killing off the speculative thread, yielding non-strict semantics identical to lazy evaluation without retaining lazy thunks. pH also introduced concurrency primitives such as MVars and IVars long before they were adopted by GHC. However, because monads were not yet widely understood at the time, pH introduced these synchronization variables without a monadic wrapper, resulting in a notoriously messy programming model.
- Professor Arvind insisted on the formal phrasing of the Haskell 98 standard: “It didn't say that from the beginning; it said lazy, but Arvind, who was there, he objected to this and said it shouldn't say lazy, it should say non-strict, because he had exactly this kind of implementation in mind.” - pH achieved non-strict semantics through fine-grained parallelism, speculative thread execution, and scheduler-based thread termination. - MVars and IVars were incorporated around 1995 without monadic typing, making concurrent state side-effects chaotic to manage.
[08:25](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=505s) Bluespec: Compiling Haskell into Hardware
Around 2000, Augustsson worked on Bluespec, an innovative hardware description language (HDL) conceived by Rishiyur S. Nikhil. In its original design, Bluespec was pure Haskell, operating as a distinct two-level language. The entire functional Haskell language was available as an elaboration and metaprogramming stage executed strictly at compile time. This compile-time metaprogramming phase unrolled data paths and generated the concrete hardware description, which operated under an entirely different execution model based on guarded atomic actions. The catch was that any compile-time functional code had to terminate; if a programmer wrote an infinite loop, the hardware synthesis compiler simply hung indefinitely.
Bluespec also pioneered advanced type-level programming features years before they appeared in mainstream Haskell. Because digital circuit designers work constantly with fixed-width bit registers, the type system supported type-level natural numbers and arithmetic constraints. Concatenating a register of length `n` with a register of length `m` yielded a register of length `n + m`, verified at compile time using type classes that solved arithmetic relations. When Bluespec spun off into an independent venture-backed commercial company, venture capitalists conducted due diligence and discovered that electrical engineers balked at Haskell's syntax. Consequently, Bluespec abandoned its Haskell-style syntax in favor of a SystemVerilog-like facade, even though the underlying semantics remained purely functional.
- Bluespec utilized full Haskell as a compile-time macro language to generate structural circuit designs. - It introduced type-level naturals and arithmetic constraints to track bit-vector sizes statically. - As Augustsson recounted regarding the commercial pivot to SystemVerilog syntax: “The venture capitalists, they did some due diligence, and they found out that hardware people were not really interested in Haskell syntax; they wanted something they could actually read.”
[11:59](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=719s) Mu: A Pragmatic, Strict Dialect at Standard Chartered
Augustsson turns to his latest Haskell compiler implementation, an internal dialect created at Standard Chartered Bank known as Mu. Rather than developing another compiler for fun, Mu was created out of specific institutional and performance constraints. Unlike standard Haskell, Mu is strict by default, departing from lazy evaluation. Augustsson argues that lazily evaluated data structures create friction and unpredictability in enterprise computing, so Mu evaluates arguments eagerly unless explicitly deferred.
A second critical departure in Mu is its handling of text. In standard Haskell, `String` is defined as a lazy linked list of characters (`[Char]`). In Mu, strings are a distinct, compact abstract data type, and the standard list-of-characters definition is rejected entirely. Augustsson identifies this design difference as the primary obstacle preventing Standard Chartered from taking off-the-shelf packages from Hackage, as nearly all standard libraries assume list operations work on strings. Furthermore, Mu omits Haskell’s arbitrary-precision `Integer` type by default, preferring fixed-width integers and floats suited to financial algorithms.
- Mu is evaluated strictly by default, discarding pervasive lazy evaluation. - Reflecting on Haskell's legacy string representation, Augustsson remarked: “When Haskell was designed, I thought it was a great idea to have String be a list of characters; now I think that was a big mistake, it should be an abstract type.” - The incompatibility between abstract strings and Hackage's `[Char]` prevents direct reuse of standard open-source Haskell packages. - Arbitrary-precision `Integer` values are absent from the core dialect in favor of machine-sized numeric types.
[14:31](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=871s) Recursion Pragmas and the SafeIO Monad
One of the most surprising features of Mu is that recursion is disabled by default. While functional programming is often taught as being synonymous with recursive functions, Augustsson points out that the primary users of Mu inside the bank are financial quantitative analysts and traders. These practitioners do not write manual recursive loops over recursive data types; instead, they operate on tabular arrays of records, numerical matrices, and relational datasets. To prevent infinite loops and encourage idiomatic bulk operations, the platform supplies rich combinator libraries for manipulating arrays and relational data. If an engineer genuinely requires explicit recursion, they must opt in by writing the compiler directive `{-# LANGUAGE Recursion #-}` at the top of their file. An audit of the bank's production codebase revealed that only about 6% of all Mu modules ever enable recursion.
```mermaid flowchart TD accTitle: Effect Boundaries in Mu and Excel accDescr: Comparison of side-effect classifications showing how Mu mirrors Microsoft Excel's distinction between pure, volatile read, and write effects. subgraph ExcelEffects["Microsoft Excel Model"] E_Pure["Pure Functions<br/>Deterministic calculation"] E_Volatile["Volatile Functions<br/>e.g., RAND(), NOW() (Read-only)"] E_Write["Macros / Commands<br/>VBA side effects & sheet writes"] end subgraph MuMonads["Mu Language System"] M_Pure["Pure Expressions<br/>Mathematical transformations"] M_SafeIO["SafeIO Monad ('I')<br/>Read market data / environment"] M_IO["Full IO Monad<br/>Write database / network side-effects"] end E_Pure <--> M_Pure E_Volatile <--> M_SafeIO E_Write <--> M_IO ```
Mu also adapts Haskell’s effect system to integrate cleanly with Microsoft Excel, one of the bank’s primary user interfaces. Excel maintains a more fine-grained effect model than standard Haskell, separating calculations into pure functions, volatile functions with read-only effects (such as fetching live market prices), and mutating commands with write effects. To reflect this hierarchy, Mu splits the standard `IO` monad into two tiers: standard `IO` and `SafeIO` (which Augustsson suggests should really just be named `I` for input). Code executing within `SafeIO` can observe external environment states and read live data streams, but it cannot trigger mutations or external actions.
- Only roughly 6% of Standard Chartered's production modules activate the `{-# LANGUAGE Recursion #-}` pragma: “I checked our modules, by the way, so it's about six percent of the modules that actually turn on recursion.” - Core computational primitives and performance-critical operations in Mu are implemented under the hood in C++. - The `SafeIO` monad prevents unauthorized mutations in analytical formulas: “You can't fire the missiles in the SafeIO monad; you can only read things from the world.” - The two-tier IO model mirrors Excel's boundary between pure cell formulas, volatile read-only inputs, and state-altering spreadsheet macros.
[17:31](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1051s) Strict Evaluation, Non-Recursion, and Lists as Arrays
Lennart Augustsson illustrates the differences in Mu by giving the example of a `getArgs` function that retrieves command-line arguments, highlighting how standard assumptions break down when transitioning away from pure laziness. When an audience member asks whether there is an inherent connection between the language being strict and the absence of general recursion, Augustsson is skeptical: “No, I don’t think so. Well, maybe there is, but I don’t know what the connection is.” The questioner clarifies that the major divergence between strict and lazy paradigms typically manifests in the programming style used when composing many loop-like components. Augustsson agrees that if a programmer is not assembling chains of loop-like constructs, the practical difference narrows significantly.
Turning to data representations, Augustsson points out that what appears syntactically as a list in Mu is actually an array under the hood. Arrays carry fundamentally different cost models compared to linked lists: indexed element access is $O(1)$, but concatenation and consing are not genuinely constant time. Because Mu avoids recursion, developers do not construct sequences by iteratively consing elements onto the head; instead, they operate on data structures in bulk. As a consequence, the divergent operational profile of arrays rarely poses an issue in practice.
- Mu code is upwardly compatible with standard Haskell: running Mu code in Haskell requires only importing a compatibility library, but the reverse does not hold. - Syntactic lists are implemented as arrays, providing constant-time random access at the cost of $O(1)$ prepend operations. - The absence of general recursion encourages bulk transformations over incremental cons-cell construction.
---
[19:57](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1197s) Semantic Freedoms and Compiler Transformations
Augustsson admits to taking notable theoretical liberties when designing the Mu compiler. In standard programming language theory, strict and lazy languages allow strictly disjoint sets of program transformations. Rather than conforming strictly to either semantic regime, the Mu compiler operates under the pragmatic assumption that all programs terminate, granting itself permission to execute whatever optimizations it prefers. As Augustsson quips to the audience: “Remember the name of the talk: Pragmatic Haskell.” When mathematically inclined colleagues object that these transformations do not preserve semantics, he freely admits they are right, but proceeds regardless.
```mermaid flowchart TD accTitle: Mu Compiler Optimization Strategy accDescr: The Mu compiler assumes total termination to apply both strict and lazy transformations freely. subgraph Input["Language Paradigms"] SL["Strict Semantics (S)"] LZ["Lazy Semantics (L)"] end subgraph Engine["Mu Compiler Engine"] Assumption["Pragmatic Axiom:<br/>Assume all expressions terminate"] Opt["Apply both S-valid and L-valid optimizations"] end subgraph Outcome["Runtime Behavior"] Dead["Dead binding elimination<br/>(discards unused trace effects)"] Reorder["Haskell-style binding reordering<br/>(independent of lexical order)"] end Input --> Assumption Assumption --> Opt Opt --> Outcome ```
These shortcuts make program transformations far simpler: “It’s much easier to do compiler transformation or program transformation if you don’t have to preserve semantics.” One direct consequence involves side effects and debugging. While Mu lacks arbitrary side-effect escape hatches like `unsafePerformIO`, it provides debug-trace primitives. If a developer places an output trace inside an unused `let` binding, the Mu compiler treats the binding as dead code and drops it completely, suppressing the expected log. Furthermore, Mu preserves standard Haskell binding semantics where declarations are mutually recursive and unordered; writing `y = x + 1` before `x = 5` causes the compiler to reorder evaluation automatically, which can occasionally disorient users accustomed to the strict, line-by-line evaluation of imperative languages.
---
[22:47](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1367s) The Cortex Architecture: Cross-Language Financial Modeling
Augustsson introduces Cortex, the foundational quant analytics platform deployed at Standard Chartered Bank. Cortex houses the core financial libraries utilized by quantitative analysts to price exotic instruments, perform risk calculations, execute Monte Carlo simulations, run partial differential equation (PDE) solvers, and ingest live market data. To meet these demands, Cortex integrates multiple programming environments into an interoperable runtime ecosystem.
```mermaid flowchart LR accTitle: Cortex Multi-Language Interoperability Architecture accDescr: Core and peripheral languages interacting via a unified binary serialization and calling convention. subgraph Core["First-Class Core"] direction TB CPP["C++"] HS["Haskell"] MU["Mu"] end subgraph Layer["Integration Layer"] direction TB ABI["Uniform Calling Conventions"] SER["Binary Serialization Protocol"] end subgraph Interop["Client / Host Environments"] direction TB CS["C#"] JV["Java"] XL["Microsoft Excel"] end Core <--> Layer Layer <--> Interop ```
The core components are implemented in C++, Haskell, and Mu, while client bindings expose the functionality to C#, Java, and Microsoft Excel. C++, Haskell, and Mu operate as first-class citizens with seamless bidirectional interoperability, while C#, Java, and Excel serve primarily as host and consumer environments. This architecture allows high-level idioms to bridge language runtimes effortlessly: a C# program can invoke a generic `map` function implemented in C++, passing it a closure defined in Mu to process an array in memory.
- Development spans a heterogeneous operating environment: desktops run Windows, while high-throughput compute farms run Linux. - Data structures and function closures are serializable using a shared binary format, enabling computations to be offloaded to remote server farms and returned. - GHC’s distributed runtime and Cloud Haskell tooling were not sufficiently mature at the time to support this model natively, driving the internal development of Mu.
---
[26:42](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1602s) Code Mobility and the Realities of Banking Infrastructure
Addressing an audience question regarding code mobility and binary format governance, Augustsson observes wryly that financial institutions are notorious for the “not invented here” mindset: “Banks are seem to be lots about not invented here... they prefer, at least the two where I’ve worked, they prefer to have done things themselves even when it’s not really a good idea.”
He explains the exact dividing line for how distributed calculations are resolved across networked compute nodes: - Compute nodes run a stable base image containing the shared infrastructure written in C++ and compiled Haskell (such as pricing kernels and PDE solvers packaged into shared libraries and DLLs with uniform entry points). - Dynamic quant business logic and custom payoff formulas are authored on top using Mu; because these scripts fluctuate constantly, they are serialized and shipped across the wire dynamically. - Attempting to serialize non-serializable structures triggers a catastrophic runtime failure, analogous to evaluating the `head` of an empty list, though internal workarounds exist for specialized use cases.
---
[29:47](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=1787s) Excel Integration: Hacking Complex Types into Four Cell Primitives
Augustsson transitions to a live demonstration showing how Mu integrates into Microsoft Excel, polling the room on who has used Excel and wished it were programmable. Projecting a spreadsheet on screen, he highlights the fundamental impediment: “The designers in their infinite wisdom decided that the world has only four types: there are numbers, strings, booleans, and errors.” To represent structured functional values within these limitations, developers must encode complex object handles inside Excel’s basic primitives.
Standard Chartered encodes functional handles as tagged strings enclosed in curly braces with an internal object identifier, such as `"{12345}"`. Augustsson compares this against the bizarre schemes adopted by other investment banks: - **Credit Suisse**: Represented complex object references as negative numbers starting at `-60,000` and counting downward, which Augustsson calls “insane” because numeric reference handles can easily be swept into spreadsheet formulas and corrupted. - **Deutsche Bank**: Cast raw 64-bit memory pointers directly into double-precision floating-point numbers to store them inside worksheet cells. - **Standard Chartered**: Uses marked strings, avoiding accidental arithmetic operations on pointer addresses.
Augustsson demonstrates compiling a Mu source module directly from within an Excel formula. The formula evaluation returns an opaque object handle representing a `SafeIO Object IModule`. He explains that module compilation is categorized within an IO monad because compiling imports requires interacting with external file storage and dependencies. To use the compiled functions inside sheet calculations, the user must explicitly invoke a loader function that instructs Excel to load the compiled module into the host process’s memory space.
[35:04](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=2104s) Writing and Executing Haskell Code Inside Excel
Lennart Augustsson demonstrates how Mu interacts directly with Microsoft Excel sheets, showing that side effects are strictly gated and cannot simply execute passively during recalculation. To run an IO action written in a cell, the user must explicitly select the cell, navigate to the custom menu, and trigger "Run Action." Once executed, the newly defined function—such as an increment helper named `demo_inc`—becomes callable inside standard spreadsheet formulas. Augustsson cautions against authoring substantive code directly inside spreadsheet cells, offering a candid critique of the interface: “Honestly Excel is about the worst text editor ever.” For expositional purposes, two lines in a cell suffice, but in real commercial workflows, code is kept in external files and loaded into the sheet via module import commands.
He then illustrates the deep integration between Mu and Excel’s native interface by adding comments to a cell function using standard Haskell syntax. When comments are prefixed with double dashes, Excel occasionally complains about text formatting, but once accepted and registered, Mu extracts those annotations and passes them directly into Excel's native Function Wizard dialog. As a result, non-programmer spreadsheet users browsing available functions can inspect proper documentation strings generated directly from Haskell type signatures and source comments.
[37:50](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=2270s) Higher-Order Functions and Sheet-Defined Computations
Turning to functional programming patterns, Augustsson notes that functional programming is fundamentally about abstraction, and even traditional spreadsheet users intuitively grasp that endlessly copying formulas across rows and columns is brittle and inadequate. However, Excel fundamentally lacks first-class functions. To map a freshly compiled Mu function over an array of numbers like `1, 2, 3`, Mu wraps functions as first-class objects within the grid. Augustsson wrestles briefly with keybindings and keyboard layouts on his demo machine, remarking with amusement on the prowess of dedicated financial modelers: “Excel power users are amazing to watch; there are secret key combinations that do all kinds of weird things.”
```mermaid flowchart LR accTitle: Sheet-Defined Function Flow accDescr: Mapping an Excel cell computation into a first-class Mu function object that can be passed to higher-order combinators. subgraph Sheet["Excel Grid"] In["Input Cell (x)"] --> Calc["Formula: 2*x + 1"] --> Out["Output Cell f(x)"] end subgraph MuSystem["Mu Runtime Engine"] Calc --> Extr["Extract Cell Range as Function"] Extr --> FirstClass["First-Class Function Object"] end FirstClass -->|"Map over array [1, 2, 3]"| Result["Result Array"] ```
Augustsson then unveils a unique, non-Mu-specific feature tailored for spreadsheet mental models: "sheet-defined functions." Excel users naturally think by chaining computations across cells. Mu allows a user to write an ordinary formula—such as `2 * x + 1` across an input cell and an output cell—and then capture that entire grid computation as a first-class function object. If a formula spans multiple input or output cells, the runtime packages them as tuples. That sheet-derived function can then be handed directly to higher-order combinators like `map`, eliminating repetitive cell formula copying while preserving the visual, interactive way quantitative analysts construct models.
[42:46](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=2566s) User-Controlled Native JIT Compilation and Partial Evaluation
Mu normally compiles code into an intermediate representation (IR) that is interpreted by an abstract machine, but high-throughput financial calculations often demand bare-metal speed. Rather than applying a global heuristic, Mu places native compilation directly under user control via a compiler hook. When passed an arbitrary value or function, the compiler attempts to produce a functionally identical, natively executed replacement:
> “If you give it five it's kind of uninteresting because five is about the fastest they get, but if you give it a function it will take that function and it will turn it into machine code; it does this by generating LLVM IR and then using the LLVM JIT.”
Augustsson notes that if a user attempts to call native compilation on a C++ function that is already linked into the environment, or on an already-compiled artifact, Mu returns the original function unchanged and emits a warning in a monitor window stating that it could not inspect or recompile it, because there is no accessible intermediate representation for opaque foreign code.
Crucially, the JIT compiler handles partial applications by performing runtime partial evaluation. When a user provides only the first argument to a two-argument function and requests native compilation, the compiler does not simply wrap the function in a closure; it folds the argument directly into the function body, specializes the intermediate representation, propagates constants, and eliminates dead branches before generating machine code. When an audience member asks whether ordinary users know where to place these compilation primitives, Augustsson concedes:
> “Probably not; so I mean people who just see this as some kind of hammer—'this is running slowly, let me see what happens if I say compile, bang, that's a bit better'—you know, but where performance really matters means they can talk to people who understand what happens and we can help a little bit where we put these things.”
[46:54](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=2814s) IR Serialization and Remote Farm Execution
An audience member asks whether Mu retains intermediate representation for all functions across their entire lifecycle. Augustsson confirms that the runtime preserves the IR for every interpreted and sheet-defined function, which unlocks cross-platform distributed execution. If a user needs to serialize a function to disk or ship an intensive job across the network from an x86 workstation to a PowerPC compute farm, Mu does not transmit target-specific machine code; it serializes the platform-independent intermediate representation. Upon arrival at a remote node, the receiving host deserializes the IR and code-generates native machine instructions tailored to its local architecture on the fly.
Augustsson demonstrates this on his sheet-defined function. He adjusts the formula to `3 * x + 1`, adds an internal debug flag, and shows how calling `funCompile` transforms the cell calculation into specialized machine code. Once compiled, the function can be mapped locally over an array, or passed to a distributed `map` primitive that transparently sends the workload to a compute cluster:
```mermaid flowchart TD accTitle: Distributed Mu Execution Architecture accDescr: How Mu functions are serialized as intermediate representation and compiled via LLVM JIT across local and remote platforms. subgraph Client["Client Machine (e.g. x86 Desktop)"] Src["Haskell / Sheet-Defined Function"] --> IR["Intermediate Representation (IR)"] IR --> LocalJIT["LLVM JIT Compiler"] --> LocalBin["x86 Machine Code"] end subgraph Cluster["Remote Compute Farm (e.g. PowerPC Node)"] IR -->|"Network Serialization"| Deser["Deserialized IR"] Deser --> RemoteJIT["LLVM JIT Compiler"] --> RemoteBin["Native Target Code"] end ```
- **IR ubiquity:** All Mu functions retain their intermediate form, enabling runtime inspection, partial evaluation, and cross-compilation. - **Foreign functions:** C++ functions linked into Mu cannot be partially evaluated or recompiled because their internal representation is opaque. - **Distributed farm mapping:** Replacing a standard `map` with a farm-enabled map serializes the function IR and data slices, dispatches them to remote servers, and gathers the reduced results seamlessly back into Excel.
[50:22](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3022s) Strict vs. Lazy: Stack Traces, Resources, and Compositionality
Augustsson shifts to a philosophical and pragmatic comparison of strict versus lazy evaluation, deflating the dogmatism that often surrounds the debate: “So I just wanted to say something about strict versus lazy, which is sort of, oh, it's this fantastic battle between good versus evil—I'm not saying which is which—but actually it doesn't really matter that much, I mean it kind of works in both worlds.” Both approaches have significant trade-offs, but in industrial software engineering, strict evaluation provides distinct operational advantages.
Foremost among these advantages are actionable stack traces. Because Mu is strict, when a runtime failure occurs, the engine outputs an exact, chronological call chain explaining which function called which. Augustsson emphasizes the productivity impact of this feature:
> “It's the single most time-saving thing, immediately, compared to Haskell, because things do go wrong and then it's so great to see exactly what the call chain was and then you can fix it.”
Furthermore, strictness provides what Augustsson terms "resource compositionality." In a strict language, developers can determine the computational complexity and memory footprint of two independent subroutines and compose them with confidence that the combined resource footprint will simply be their sum. In a lazy language, composing an element generator `[1..n]` with a fold like `sum` might execute in constant space or blow the heap with unevaluated thunks, varying entirely on “how the strictness analyzer felt that day.” Similarly, exception handling behaves reliably under strict semantics, whereas in lazy Haskell, catching an error around an unforced expression does not actually trap the failure until evaluation is triggered elsewhere.
However, strictness sacrifices "semantic compositionality." Augustsson points out classic idioms like `any p`, which naturally halts the moment a predicate matches when composed over an infinite or lazy list. In a strict language, that separation of generation from consumption breaks down unless explicitly encoded with bespoke control flow. He concludes that while laziness represents a theoretically cleaner and more expressive paradigm for writing modular code, its unpredictable resource consumption remains a persistent hazard in production systems.
[53:36](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3216s) Call-by-Name in a Strict Haskell (`Mu`)
Lennart Augustsson explains that even in a fundamentally strict language, certain core primitives must evaluate lazily. He observes that even C contains lazy semantics in short-circuiting logical operations, where writing `false && error` will never evaluate the second operand. In Mu, his strict Haskell dialect at Standard Chartered Bank, this behavior is exposed to programmers via the `by-name` keyword. Rather than relying on fragile compiler macros with risk of variable capture, `by-name` provides hygienic call-by-name macro expansion semantics. While languages like C, C++, Standard ML, and OCaml hardcode such short-circuiting behaviors directly into the compiler as privileged syntax, Augustsson wanted regular developers to be able to define their own non-strict combinators.
```mermaid flowchart TD accTitle: Macro Expansion of Composition in Mu accDescr: Flow diagram showing how Mu evaluates call-by-name function composition to avoid duplicated evaluation of arguments.
subgraph Problematic["Naive Inlining"] A["(reallySlow f). g"] --> B["\x -> (reallySlow f) (g x)"] B --> C["Evaluates reallySlow inside lambda per call!"] end
subgraph MuExpansion["Mu by-name Expansion"] D["by-name (.) f g"] --> E["let f' = f; g' = g in \x -> f' (g' x)"] E --> F["reallySlow evaluated outside lambda once"] end ```
He offers a word of caution born from implementation headaches: “By the way if you ever try, if you want to design a language, never use call by name as your calling convention.” He illustrates this using standard Haskell function composition, `(f. g) x = f (g x)`. If `.` is declared `by-name` and naively expanded into an expression like `reallySlow f. g`, the expensive computation `reallySlow` gets inlined directly inside the generated lambda body, repeating the work on every invocation. To fix this, Mu's definition of `.` binds the arguments in an outer `let` binding (`let f' = f; g' = g in \x -> f' (g' x)`) before referencing them beneath the lambda abstraction.
[56:04](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3364s) Pragmatic Laziness and What Breaks in Strict Mode
Across the entire codebase, only roughly twenty definitions require an explicit `by-name` annotation. These include standard control-flow utilities like `when`, `unless`, and `fromMaybe`. Interestingly, users without any prior functional programming experience still intuitively expect these combinators to evaluate conditionally. Augustsson recalls developers complaining when `fromMaybe` failed in Mu because an `error` call passed as the fallback default value triggered immediately upon application.
He also catalogs the recurring pain points where Haskell's default laziness cannot easily be recovered in a strict dialect: - **Lazy bindings:** Expensive computations used conditionally inside functions (such as a mapped closure) need to execute zero or one time, not unconditionally up front. - **Infinite structures:** Idioms like `zipWith` over an infinite list `[1..]` cannot execute and will diverge. - **Type proxies:** In the absence of explicit type applications, passing dummy values like `undefined` solely to carry type witness information triggers runtime bottom crashes unless marked with lazy bindings. - **Cyclic data structures:** Defining knot-tying graphs or self-referential structures directly does not work in strict evaluation. - **Idiomatic Haskell abstractions:** Common decoupled idioms separating generation from consumption fail to terminate or require manual chunking.
[58:27](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3507s) Scale, Naive Users, and Precise Error Locations
Augustsson reviews the scale of the deployment at Standard Chartered. The codebase encompasses roughly 400,000 lines of Mu code across approximately 2,400 modules, maintained by a broad mix of quantitative researchers and domain specialists. Augustsson characterizes many of them as mathematically sophisticated but computer science-naive users: “They don't write things like test cases and sometimes you really twist their arm, so it's good to have strict type checking to at least get some of the bugs out early.” Developers are strictly prohibited from checking in changes without running the type checker across the entire 400,000-line repository, a process that completes in just 30 seconds on his desktop.
```mermaid flowchart LR accTitle: Two-Location Type Checking Diagnostics accDescr: Visual breakdown of how Mu highlights conflicting source locations for type errors.
subgraph ErrorReport["Type Conflict Report"] direction TB E1["Bool vs String mismatch"] L1["Location 1: Red highlight (source of Bool)"] L2["Location 2: Blue highlight (source of String)"] E1 --> L1 E1 --> L2 end
subgraph Traditional["Traditional GHC Report"] G1["Point of discovery"] G2["'Useless order-dependent location'"] G1 -.-> G2 end ```
He credits the low frequency of complaints about type errors to diagnostic clarity that improves on GHC. Rather than pointing solely at the arbitrary traversal location where unification failed—which Augustsson dismisses as “completely useless” because it merely reflects compiler traversal order—Mu highlights the two concrete, conflicting source spans simultaneously (for instance, showing in red where a `Bool` originated and in blue where a `String` originated). In expressions like `1 + True`, it pinpoints both the `Num` constraint originating from `1` and the boolean type introduced by `True`, enabling users to instantly spot and correct discrepancies.
[01:01:25](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3685s) Tooling: Linter, IDE, and Hiding the Compiler
To keep naive developers productive, the team built a tailored developer toolset around Mu: - **Mu Lint:** Adapted from Neil Mitchell’s HLint, augmented with proprietary bank-specific rules. Augustsson quips: “It's nicer to get critique from the machine than from people.” - **Interactive REPL:** A read-eval-print loop analogous to GHCi for rapid interactive experimentation. - **IDE with Rich Introspection:** An editor providing type hover inspections, identifier auto-completion, browsing, and an interactive debugger. Augustsson observes with amusement that while users initially insisted “we must have a debugger, we must have a debugger,” hardly anyone actually touches it once delivered. - **Transparent Compilation:** The build pipeline mimics a dynamic scripting language. Users point at raw source files and execute them directly without managing intermediate binaries, compilation artifacts, or Makefiles. As Augustsson emphasizes: “It's running the program that is the interesting thing, not compiling.”
[01:03:56](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3836s) Q&A: Financial Workloads, User Adoption, and Hardware Compilation
Answering audience questions, Augustsson explains the day-to-day work handled by Mu: - **Typical Workloads:** Scripts routinely ingest disparate records from multiple databases, massage financial inputs, pass structured grids to external C++ partial differential equation (PDE) solvers, calculate derivative pricing and risk metrics, and assemble polished reports. Intensive floating-point operations are deferred across the FFI into C++, positioning Mu as a type-safe glue language. - **Spreadsheet Integration:** While earlier demonstrations highlighted rich Excel integration, the bulk of the 400,000 lines runs headlessly outside of spreadsheets. - **Adoption Across Roles:** Formal quantitative analysts remain split, with many still clinging to C++. Surprisingly, adoption proved strongest among front-office traders who were never hired to program: “It's kind of interesting to go walk out on the trading floor and see Graham Hutton's book there next to someone who's trading precious metals.” - **Learning Curve:** Because Mu is an internal proprietary language, users learn primarily by reading Graham Hutton's *Programming in Haskell* alongside existing bank repositories—though Augustsson concedes that existing code often serves as a poor template. - **Targeting FPGAs:** When asked whether he would ever compile Mu directly to FPGA hardware rather than targeting LLVM bytecode, Augustsson rejects the premise outright: “I don't think that will work. I think if you want to design hardware you need to have some clue about what you're doing.”
## Notable quotes > [11:59](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=719s) When Haskell was designed, I thought it was a great idea to have String be a list of characters; now I think that was a big mistake, it should be an abstract type. — Lennart Augustsson
> [01:01:25](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3685s) It's nicer to get critique from the machine than from people. — Lennart Augustsson
> [08:25](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=505s) The venture capitalists, they did some due diligence, and they found out that hardware people were not really interested in Haskell syntax; they wanted something they could actually read. — Lennart Augustsson
> [53:36](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3216s) By the way if you ever try, if you want to design a language, never use call by name as your calling convention. — Lennart Augustsson
> [01:01:25](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=3685s) It's running the program that is the interesting thing, not compiling. — Lennart Augustsson
> [14:31](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=871s) You can't fire the missiles in the SafeIO monad; you can only read things from the world. — Lennart Augustsson
> [35:04](https://www.youtube.com/watch?v=hgOzYZDrXL0&t=2104s) Honestly Excel is about the worst text editor ever. — Lennart Augustsson
## Mentioned Lennart Augustsson, Standard Chartered Bank, Commercial Users of Functional Programming (CUFP), Thomas Johnsson, Chalmers University of Technology, Lazy ML (LML), HBC, Don Stewart, Glasgow Haskell Compiler (GHC), Arvind, Rishiyur S. Nikhil, Parallel Haskell (pH), Massachusetts Institute of Technology (MIT), Bluespec, SystemVerilog, Mu, Hackage, Microsoft Excel, Cortex, C++, C#, Java, Credit Suisse, Deutsche Bank, LLVM, Neil Mitchell, HLint, Graham Hutton
## Video description Keynote: Pragmatic Haskell
Saturday, September 24, 2011 - 09:00 AM - 10:00 AM Speakers:
Lennart Augustsson Standard Chartered Bank Abstract:
Over the years I've both used Haskell a lot commercially and also made four Haskell implementations. Of the four implementations three are very non-standard because they were aimed for particular targets. I will talk about various aspects of using and implementing non-standard Haskell as well as show a number of examples from my latest Haskell implementation which is strict. I will also make a pragmatic assessment of strict versus lazy.
[Source](https://www.youtube.com/watch?v=hgOzYZDrXL0)