FunctionalXpp
Functional Programming Patterns in Modern C++ · 2016–2019
Why Functional C++?
C++ is not a functional language. But the patterns that make Haskell powerful — monadic composition, lazy evaluation, persistent data structures, parser combinators — are not language-specific ideas. They are design patterns that encode deep truths about how computation composes. FunctionalXpp asks: can these patterns be expressed in modern C++ without sacrificing type safety or performance?
This library was built during 2016–2019 as a deliberate study of functional programming through implementation. The discipline of translating Haskell abstractions into C++17 template metaprogramming forces a precise understanding of what each abstraction does versus what it merely looks like. You cannot implement a monad in C++ by accident.
Core Abstractions
Maybe Monad
A type-safe optional value that eliminates null pointer errors through the type system:
Maybe<int> result = Some(42);
Maybe<int> nothing = None<int>();
// Monadic chaining — computation short-circuits on None
auto doubled = result.map([](int x) { return x * 2; });
auto filtered = result.filter([](int x) { return x > 10; });
The key insight: Maybe<T> isn't just std::optional with a different name. It's a functor that supports map, a monad that supports bind (>>=), and these operations compose. Error propagation becomes implicit in the type structure rather than explicit in control flow.
Persistent Functional List
An immutable cons-cell linked list with structure sharing via smart pointers:
List<int> xs = make_list(1, 2, 3, 4, 5);
List<int> ys = cons(0, xs); // ys shares xs's tail — O(1) prepend
int h = head(xs); // 1
List<int> t = tail(xs); // [2, 3, 4, 5]
Persistent data structures are the backbone of functional programming. By making data immutable and sharing structure between versions, they eliminate a class of concurrency bugs while enabling efficient “copy-on-write” semantics through pointer sharing.
Parser Combinators
A combinator framework inspired by Graham Hutton's Programming in Haskell, where complex parsers are built from simple atomic parsers through composition:
// Atomic parsers
auto digit = satisfy(isdigit);
auto letter = satisfy(isalpha);
// Combinators: sequence (&), alternative (|), repetition
auto identifier = (letter & many(letter | digit));
auto number = many1(digit);
The parser type is itself monadic: Parser<T> is a function from input to ParsedResult<T>, and the bind operator (>>=) sequences parsers while threading state. This means arbitrary parsers compose without explicit state management — the monad handles it.
Lazy Views with CRTP
A chainable, lazy evaluation framework using the Curiously Recurring Template Pattern:
std::vector<int> nums{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto result = fxpp::view(nums)
.filter([](int x) { return x % 2 == 0; }) // lazy
.map([](int x) { return x * x; }) // lazy
.collect(); // [4, 16, 36, 64, 100]
Operations chain lazily — no intermediate containers are allocated until collect() forces evaluation. The CRTP pattern allows each view layer (MapHeadedView, FilterHeadedView, FlatMapHeadedView) to inherit chainable methods without virtual dispatch overhead. This predates C++20 ranges by several years but explores the same design space.
Template Metaprogramming Techniques
The library exercises several advanced C++ techniques:
| Technique | Where Used | Purpose |
|---|---|---|
| CRTP | View system | Static polymorphism for zero-cost method chaining |
| SFINAE | Container operations | Constrain template instantiation to valid types |
| Variadic templates | make_list, PTree | Type-safe variadic construction |
| Type traits | Throughout | Compile-time type inspection and transformation |
| Operator overloading | Monadic operators | >>= for bind, & for tuple creation, | for alternatives |
| Smart pointers | Persistent list | Structure sharing with automatic memory management |
Hierarchical Property Trees
A compile-time hierarchical data structure for type-safe nested configuration:
// Type-safe tree: keys and value types checked at compile time
using Config = PTree<
PLeaf<int, "port">,
PNode<"database",
PLeaf<std::string, "host">,
PLeaf<int, "connections">
>
>;
Invalid key access becomes a compile error rather than a runtime crash. This pattern — encoding domain constraints in the type system — reappears throughout the later MayaPortal project where type signatures encode scientific propositions.
The Thread to MayaPortal
FunctionalXpp's abstractions reappear, matured, in MayaPortal's rendering engine:
| FunctionalXpp (2016–2019) | MayaPortal (2025–present) |
|---|---|
Maybe<T> monad | std::expected<T, Error> with monadic and_then |
| Lazy views with CRTP | Pure-core / effectful-shell architecture |
| Parser combinators with bind | Rendering pipeline as typed compositions |
| Persistent data structures | Immutable GPU context (Reader pattern) |
| Operator overloading for DSL | Monadic vocabulary: Reader, State, Expected, Writer |
The progression is clear: FunctionalXpp explored whether functional patterns could work in C++. MayaPortal applies them to a real domain — GPU rendering — where composability and type safety directly improve the ability to reason about complex state transformations.
Technical Details
- Header-only — no compilation required; include and use
- Build system — Waf (Python-based), supporting GCC 5+, GCC 6+, Clang with C++17
- Testing — Catch2 framework, 16 test files covering all major components
- Dependencies — C++ standard library only (GSL for some numerical utilities)
- Source — github.com/visood/FunctionalXpp