Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions docs/choice/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ title: Choice monad
:include-doxygen-doc: fn::choice

## choice_for {style: "api"}
Used to generate `fn::choice` correctly. Sorts and deduplicates types under the hood.

Some text explaining this section...
The construction alias: accepts alternatives in any order, with duplicates and nested copacks,
and resolves to the canonical `fn::choice`. Prefer it over spelling `choice` directly, so that no
spelling in your project is tied to one compiler's alternative order.

:include-doxygen-member: fn::choice_for { signatureOnly: false, includeAllMatches: true }

## choice {style: "api"}
Some text explaining this section...
Construction: from a value of one alternative, in place from arguments, or widening from a
`copack` over a subset of the alternatives.

:include-doxygen-member: fn::choice { signatureOnly: false, includeAllMatches: true }

## value {style: "api"}
Some text explaining this section...
The alternatives as the underlying `copack`, always present.

:include-doxygen-member: fn::choice< Ts... >::value { signatureOnly: false, includeAllMatches: true }
26 changes: 26 additions & 0 deletions docs/composition/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: Composition
---

##### Defined in {style: "api", badge: "#include <fn/pack.hpp>"}
Comment thread
Bronek marked this conversation as resolved.

---

Independent computations compose side by side. The conjunction `a & b` keeps both results:
values multiply into a `pack` and errors sum into a `copack`, with the leftmost failing operand's
error held at runtime. The disjunction `a | b` keeps the first that worked: values sum into a
`copack` and errors multiply into a `pack`, present only when every operand failed. Each
carrier's header declares its own `&` and `|` operators; this header defines their n-ary folds
`fn::conjoin` and `fn::disjoin`.

## conjoin {style: "api"}
:include-doxygen-doc: fn::conjoin_t

## conjoin call signatures {style: "api"}
:include-doxygen-member: fn::conjoin_t::operator() { signatureOnly: false, includeAllMatches: true }

## disjoin {style: "api"}
:include-doxygen-doc: fn::disjoin_t

## disjoin call signatures {style: "api"}
:include-doxygen-member: fn::disjoin_t::operator() { signatureOnly: false, includeAllMatches: true }
11 changes: 11 additions & 0 deletions docs/copack/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,14 @@ title: Copack
:include-doxygen-doc: fn::copack

:include-doxygen-doc: fn::copack_for

## Apply {style: "api"}
Elimination: the active alternative routes into the callable, exhaustively - every alternative
must have a viable arm.

:include-doxygen-member: fn::copack< Ts... >::apply { signatureOnly: false, includeAllMatches: true }

## Transform {style: "api"}
The self-flattening map over the alternatives: the branch results form a new normalized copack.

:include-doxygen-member: fn::copack< Ts... >::transform { signatureOnly: false, includeAllMatches: true }
25 changes: 25 additions & 0 deletions docs/expected/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: Expected monad
---

##### Defined in {style: "api", badge: "#include <fn/expected.hpp>"}

---

:include-doxygen-doc: fn::expected

## expected_unit {style: "api"}
The graded gateway: initiating a pipeline with this unit trigger opts all subsequent `and_then`
steps into graded error-set unioning, with no fake starting errors.

:include-doxygen-member: fn::expected_unit { signatureOnly: false, includeAllMatches: true }

## copack_error {style: "api"}
The explicit lift into the graded world, on the error side.

:include-doxygen-member: fn::expected::copack_error { signatureOnly: false, includeAllMatches: true }

## copack_value {style: "api"}
The same lift, on the value side.

:include-doxygen-member: fn::expected::copack_value { signatureOnly: false, includeAllMatches: true }
8 changes: 4 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ Functional programming in C++

## Why

The purpose of this library is to exercise an approach to functional programming in C++ on top of the existing `std` C++ `std::expected` and `std::optional`, with the aim of eventually extending the future versions of the C++ standard library with the functionality found to work well.
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 `std::expected` and `std::optional`), with the aim of eventually extending future revisions of the C++ standard library with the functionality found to work well.

## How

The approach is to take the existing `std` types in the C++ standard library (when appropriate) and extend them (via inheritance) with the facilities useful in writing functional style programs. Eventually, the proposed functionality will be (hopefully) folded into the existing `std` types and new `std` types will be added.
The library comes as two parts: `pfn` (namespace `pfn`) is a faithful polyfill of the standard vocabulary types as specified for C++26, available to a C++20 compiler; `fn` (namespace `fn`) extends them with the facilities useful in writing functional style programs and adds new vocabulary types. Every `fn` type with a `pfn` counterpart is a strict superset of it: switching a valid program from `pfn` types to `fn` changes neither compilation nor behaviour.

## What

The library provides following utilities:
The library provides the following utilities:

* functors - extensible system of encapsulation of monadic operations, expressed with a pipe `operator |`
* copack - coproduct of types (a sum of types), similar to `std::variant` but indexed by type rather than order, composes with the product of types
* choice monad - monad built on top of the coproduct of types, dispatch by overloading rules
* pack - product of types, similar to `std::tuple`, composes with the coproduct of types
* composition - monadic types containing arbitrary types, products or coproducts can be combined with `operator &`
* composition - monadic computations combined side by side: conjunction with `operator &`, disjunction with `operator |`, and their n-ary folds `fn::conjoin` and `fn::disjoin`
* multidispatch - dispatch any valid combination of product(s) and coproduct(s) to a function, based on overloading rules
* graded monad - integrate coproduct into `optional` and `expected` monads, enables extensible `expected` error types
* ... and more
Expand Down
11 changes: 11 additions & 0 deletions docs/just/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,14 @@ title: Just
---

:include-doxygen-doc: fn::just

## value {style: "api"}
The payload, always present: the access is total, never throwing.

:include-doxygen-member: fn::just::value { signatureOnly: false, includeAllMatches: true }

## transform {style: "api"}
:include-doxygen-member: fn::just::transform { signatureOnly: false, includeAllMatches: true }

## and_then {style: "api"}
:include-doxygen-member: fn::just::and_then { signatureOnly: false, includeAllMatches: true }
24 changes: 24 additions & 0 deletions docs/multidispatch/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: Multidispatch
---

##### Defined in {style: "api", badge: "#include <fn/functional.hpp>"}
Comment thread
Bronek marked this conversation as resolved.

---

Elimination of the algebraic structures: `fn::apply` unpacks products and dispatches over
alternatives by ordinary C++ overload resolution, and `fn::overload` fuses per-alternative
lambdas into one overload set. Dispatch is exhaustive: an alternative without a viable arm makes
the whole call not applicable.

## apply {style: "api"}
:include-doxygen-member: fn::apply { signatureOnly: false, includeAllMatches: true }

## apply_r {style: "api"}
:include-doxygen-member: fn::apply_r { signatureOnly: false, includeAllMatches: true }

## overload {style: "api"}

##### Defined in {style: "api", badge: "#include <fn/utility.hpp>"}

:include-doxygen-doc: fn::overload
14 changes: 14 additions & 0 deletions docs/optional/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: Optional monad
---

##### Defined in {style: "api", badge: "#include <fn/optional.hpp>"}

---

:include-doxygen-doc: fn::optional

## copack_value {style: "api"}
The explicit lift into the graded world.

:include-doxygen-member: fn::optional::copack_value { signatureOnly: false, includeAllMatches: true }
3 changes: 2 additions & 1 deletion docs/optional/transform_error.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ title: fn::transform_error
---

## Return value {style: "api"}
A monadic type of the same kind.
Not applicable: `transform_error` is rejected on `optional`, which has no error value to map.
Use `or_else` to act on the empty state instead.
5 changes: 3 additions & 2 deletions docs/pack/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ title: Packs
:include-doxygen-doc: fn::pack

## Append {style: "api"}
Some text explaining this section...
Grows the product without nesting: appending a value adds one field, and appending a pack
splices its fields in.
:include-doxygen-member: fn::pack::append { signatureOnly: false, includeAllMatches: true }

## Apply {style: "api"}
Some text explaining this section...
Elimination: the elements spread into a callable as separate arguments.
:include-doxygen-member: fn::pack::apply { signatureOnly: false, includeAllMatches: true }
28 changes: 28 additions & 0 deletions docs/pfn/expected.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: Expected polyfill
---

##### Defined in {style: "api", badge: "#include <pfn/expected.hpp>"}

---

:include-doxygen-doc: pfn::expected

## expected over void {style: "api"}
The partial specialization serving computations which succeed with no value.

:include-doxygen-doc: pfn::expected< void, E >

## unexpected {style: "api"}

:include-doxygen-doc: pfn::unexpected

## unexpect {style: "api"}

:include-doxygen-doc: pfn::unexpect_t

## bad_expected_access {style: "api"}

:include-doxygen-doc: pfn::bad_expected_access

:include-doxygen-doc: pfn::bad_expected_access< void >
25 changes: 25 additions & 0 deletions docs/pfn/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: Polyfill layer
---

The library is layered: namespace `pfn` is a faithful polyfill of standard vocabulary types and
utilities as specified for C++26, available to a C++20 compiler, and namespace `fn` builds the
functional-programming extensions on top of it. Every `fn` type with a `pfn` counterpart is a
strict superset of it: a valid program switching from `pfn` to `fn` changes neither compilation
nor behaviour.

`pfn` polyfills only what C++20 lacks: all of `<expected>`, the C++23 and C++26 additions to
`std::optional` (the monadic operations, iterator support, `optional<T&>`), `std::apply` in its
SFINAE-friendly C++26 shape together with its applicability traits, `std::invoke_r` and
`std::unreachable`. Names C++20 already has — `std::nullopt`, `std::in_place`,
`std::bad_optional_access` — are used directly and not mirrored.

The polyfills track the C++ working draft, deviating deliberately in three ways, each noted on
the entity it concerns:

* where the standard leaves a member's `noexcept` specification unstated, one is derived from
the underlying types; every such clause is marked `// extension` in the source
* the draft's hardened preconditions are checked with an assertion, customizable by defining
`LIBFN_ASSERT` before inclusion
* `expected`'s comparison against a value is declared at namespace scope rather than as a
hidden friend, keeping its constraint deducible
13 changes: 13 additions & 0 deletions docs/pfn/optional.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
title: Optional polyfill
---

##### Defined in {style: "api", badge: "#include <pfn/optional.hpp>"}

---

:include-doxygen-doc: pfn::optional

## optional over a reference {style: "api"}

:include-doxygen-doc: pfn::optional< T & >
31 changes: 31 additions & 0 deletions docs/pfn/utility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: Utilities
---

## apply {style: "api"}

##### Defined in {style: "api", badge: "#include <pfn/tuple.hpp>"}

:include-doxygen-member: pfn::apply { signatureOnly: false, includeAllMatches: true }

### Applicability traits {style: "api"}
The C++26 traits `apply` is specified through; each also comes in its `_v` (for the two
predicates) or `_t` (for the result) form.

:include-doxygen-doc: pfn::is_applicable

:include-doxygen-doc: pfn::is_nothrow_applicable

:include-doxygen-doc: pfn::apply_result

## invoke_r {style: "api"}

##### Defined in {style: "api", badge: "#include <pfn/functional.hpp>"}

:include-doxygen-member: pfn::invoke_r { signatureOnly: false, includeAllMatches: true }

## unreachable {style: "api"}

##### Defined in {style: "api", badge: "#include <pfn/utility.hpp>"}

:include-doxygen-member: pfn::unreachable { signatureOnly: false, includeAllMatches: true }
11 changes: 11 additions & 0 deletions docs/toc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
expected
index
and_then
discard
fail
Expand All @@ -11,6 +12,7 @@ expected
transform
value_or
optional
index
and_then
discard
fail
Expand All @@ -32,5 +34,14 @@ copack
index
just
index
composition
index
multidispatch
index
pfn
index
expected
optional
utility
ci
index
3 changes: 2 additions & 1 deletion include/fn/and_then.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,11 @@ constexpr inline struct and_then_t final {

struct and_then_t::apply final {
/**
* @brief TODO
* @brief Binds through the carrier's own `and_then` member
*
* @param v The monad
* @param fn The function to apply
* @return A carrier of the same kind, possibly with a widened error grade
*/
template <some_monadic_type V, typename Fn>
[[nodiscard]] constexpr auto operator()(V &&v, Fn &&fn) const //
Expand Down
Loading
Loading