Why The purpose of this library is to exercise an approach to functional programming in C on top of the existing standard vocabulary types such as and with the aim of eventually extending future revisions of the C standard library with the functionality found to work well Example What The library features demonstrated by the code example above Monadic sequences pipes a or through operations and act on the value and on the error with and more besides Graded errors each stage fails its own way a malformed string a zero denominator an out of range result and the library folds these into one whose type it derives for you here never spelled by hand Composing values gathers successful operands left to right two values become a a third appends to it A is a heterogeneous product the operands as one value spread into the next call for example in where a returned from is passed to an overload taking two numbers Composing alternatives when a side is a a co product one of several types indexed by type not by position like distributes over it pairing every alternative with the other operand Two copacks yield the full cartesian product The result type is flattened deduplicated and sorted for you Multidispatch the pack or copack of packs flows into the next stage as separate arguments An or any function dispatches on the runtime alternative by ordinary overload resolution Dispatch is exhaustive a missing handler is a compile error Identity monad cannot hold an error enforced at compile time a spelling of the identity monad the example lifts into it as No surprises libfn throws no exceptions of its own only as the standard mandates and composes safely with callables that do it allocates no memory of its own and performs no I O Being fully it can drive a program evaluated entirely at compile time where the compiler diagnoses any undefined behaviour The example also demonstrates how well libfn works with general programming idioms is a smart constructor the only way to build a enforcing the type s invariants and returning callers never need to re check what the type guarantees Treating callables as values lets operations such as accept whole carrying its overload set These properties also make libfn a natural fit for asynchronous composition such as coroutines or senders receivers Operations and monadic types alike are plain values is a description of a step executed only when a monad is piped into it an input to the sequence or the result of the preceding operation A framework can hold the steps of a computation and apply them as results arrive with a strongly typed error channel and no hidden control flow exactly what such programming models need Beyond the example a monad over the same operations over as over simultaneous disjunction using to fallback combine monadic computations and its fold for simultaneous product folds tuple protocol in or structured bindings and are both structural types a value which may be used as a template parameter support for immovable values and callables an extensible pipeline where a verb defined outside the library pipes exactly like the built in ones and more see examples and the API reference How The library comes as two parts in one repository namespace a faithful polyfill of standard library vocabulary types as specified for C 26 including the monadic functions and range support plus smaller utilities such as and It adds nothing of its own on top of what s mandated by the C standard or accepted for a future revision such as namespace the functional programming library It extends the vocabulary types with the facilities useful in writing functional style programs monadic operations composable with such as and adds new vocabulary types Every type with a counterpart is a strict superset of it switching a valid program using types to use instead changes neither compilation nor program behaviour builds on and all of libfn requires only a C 20 compatible compiler The minimum supported compilers are gcc 12 and clang 16 Apple Clang 16 0 and Microsoft Visual Studio 2022 or newer are supported as well See CONTRIBUTING md for how to set up a recent enough toolchain when your OS does not ship one Implementation note This library requires a total ordering of types which the standard provides from C 26 By default the library relies on an internal naive implementation of such a feature which is not expected to work with unnamed types types without linkage etc On a compiler implementing C 26 gcc 16 or newer the opt in mode uses the standard feature instead The two modes may order types differently so types live in a distinct ABI namespace per mode and the two modes never link as one is mode independent see CONTRIBUTING md for the mode s requirements Using the library The library is header only The CMake package exports two targets Packaging is provided and exercised by CI for conan vcpkg an in repo port Nix and Bazel plain CMake or works as well Until the first tagged release consume a pinned git revision and read Backwards compatibility Every packaging route above except Bazel also delivers the compile options the headers require Under Bazel and a plain copy of these options don t arrive automatically provide them yourself C 20 or newer in Bazel on clang initialization elides braces by design and with MSVC plus The authoritative set is the options in cmake CompilationOptions cmake Backwards compatibility The maintainers aim for compatibility with the proposed changes to the C standard library rather than with the existing uses of the code in this repo In practice this means that all code in this repo should be considered under intensive development and unstable until the standardization of the proposed facilities Versioning and ABI Releases are numbered and will stay below for the foreseeable future SemVer treats any version as unstable anything may change so libfn narrows that into a usable contract a bump in is a breaking change API and or ABI a bump in is a bug fix or a purely additive extension the API and ABI stay compatible but inline function definitions may change see below Because the library is header only use a single libfn version per binary Mixing versions in one program is an ODR violation and that includes two releases of the same line whose inline definitions may differ even though the ABI matches Contributing See CONTRIBUTING md for the development environment building testing the version bump mechanics and the pre commit workflow The design history decisions and the ideas they obsoleted is recorded in CHANGELOG md Acknowledgments Gaper Aman for providing the inspiration in Fun ctional C and the M word Bartosz Milewski for taking the time to explain parametrised and graded monads and effect systems Ripple for allowing the main author the time to work on this library License Distributed under the ISC License see LICENSE md for the terms std expected std optional Various error types enum class NotANumber enum class DivByZero enum class Overflow Operations on rational numbers enum class Add enum class Sub enum class Mul enum class Div `parse` turns a delimited string into a pair of numbers a numerator and denominator constexpr auto parse std string_view s noexcept > fn expected<fn pack<int int> fn copack<NotANumber>> class Rational int n_ d_ constexpr Rational int n int d noexcept n_ n d_ d public constexpr auto operator Rational const & const noexcept > bool default constexpr auto num const noexcept > int return n_ constexpr auto den const noexcept > int return d_ The invariants live in the type `make` is the only way to build a `Rational` and every one is reduced sign normalized and representable Callers receive a value they never need re check static constexpr struct make_t constexpr auto operator long long n long long d const noexcept > fn expected<Rational fn copack_for<DivByZero Overflow>> if d 0 return fn unexpected fn copack DivByZero if n std numeric_limits<long long> min || d std numeric_limits<long long> min return fn unexpected fn copack Overflow auto const g d < 0 1 1 * std gcd n d n g d g if n < std numeric_limits<int> min || n > std numeric_limits<int> max || d > std numeric_limits<int> max return fn unexpected fn copack Overflow return Rational static_cast<int> n static_cast<int> d constexpr auto operator std string_view s const noexcept > decltype auto return parse s | fn and_then *this make constexpr auto neg const noexcept > decltype auto return make 1LL * n_ d_ constexpr auto inv const noexcept > decltype auto return make d_ n_ constexpr auto add Rational const &other const noexcept > decltype auto return make 1LL * n_ * other d_ 1LL * other n_ * d_ 1LL * d_ * other d_ constexpr auto sub Rational const &other const noexcept > decltype auto return other neg | fn and_then *this Rational y return add y constexpr auto mul Rational const &other const noexcept > decltype auto return make 1LL * n_ * other n_ 1LL * d_ * other d_ constexpr auto div Rational const &other const noexcept > decltype auto return other inv | fn and_then *this Rational y return mul y `evaluate` parses each operand applies the operator and lets `make` re check the result Each stage fails its own way and the library folds error types into one co product constexpr auto evaluate std string_view a fn copack_for<Add Sub Mul Div> op std string_view b noexcept > decltype auto using Op fn expected<decltype op fn copack<>> return Rational make a & Op op & Rational make b | fn and_then fn overload Rational x Add Rational y return x add y Rational x Sub Rational y return x sub y Rational x Mul Rational y return x mul y Rational x Div Rational y return x div y The error type of a sequence is the derived copack of all failure modes never spelled by hand static_assert std is_same_v<decltype Rational make 1 1 fn expected<Rational fn copack_for<DivByZero NotANumber Overflow>>> static_assert std is_same_v<decltype evaluate 1 2 Add 3 4 fn expected<Rational fn copack_for<DivByZero NotANumber Overflow>>> Constant evaluated calculations used to verify both values and errors during compilation static_assert evaluate 1 2 Add 1 3 value Rational make 5 6 static_assert evaluate 2 3 Div 0 1 error has_value<DivByZero> operator| expected optional and_then transform or_else recover transform_error filter inspect fail copack copack<DivByZero NotANumber Overflow> operator& pack pack make pack<int int> parse copack std variant & fn overload expected<T copack<>> op Op value constexpr make Rational expected and_then make and_then f choice copack optional expected operator| fn disjoin fn conjoin pack get<I> p pack copack constexpr pfn include pfn pfn std expected std optional optional<T&> std invoke_r std unreachable has_error fn include fn fn operator| and_then transform or_else inspect recover filter copack choice pack fn pfn pfn fn fn pfn std type_order std type_order LIBFN_CXX26 fn pfn find_package libfn CONFIG REQUIRED target_link_libraries main PRIVATE libfn fn # or libfn pfn for the polyfills alone FetchContent add_subdirectory include cxxopt std c 20 Wno missing braces fn pack permissive _HAS_CXX23 INTERFACE 0 y z 1 0 0 0 y z y z z y