@@ -18,6 +18,7 @@ These include:
1818 * ` sync_wait() `
1919 * ` when_all() `
2020 * ` when_all_ready() `
21+ * ` fmap() `
2122* Cancellation
2223 * ` cancellation_token `
2324 * ` cancellation_source `
@@ -132,6 +133,19 @@ namespace cppcoro
132133 // the possibility of it throwing an exception.
133134 <unspecified> when_ready() const noexcept;
134135 };
136+
137+ template<typename T>
138+ void swap(task<T>& a, task<T>& b);
139+
140+ // Apply func() to the result of the task, returning a new task that
141+ // yields the result of 'func(co_await task)'.
142+ template<typename FUNC, typename T>
143+ task<std::invoke_result_t<FUNC, T&&>> fmap(FUNC func, task<T> task);
144+
145+ // Call func() after task completes, returning a task containing the
146+ // result of func().
147+ template<typename FUNC>
148+ task<std::invoke_result_t<FUNC>> fmap(FUNC func, task<void> task);
135149}
136150```
137151
@@ -243,6 +257,16 @@ namespace cppcoro
243257 // await the result.
244258 template<typename T >
245259 shared_task<T > make_shared_task(task<T > task);
260+
261+ // Apply func() to the result of the task, returning a new task that
262+ // yields the result of 'func(co_await task)'.
263+ template<typename FUNC, typename T>
264+ task<std::invoke_result_t<FUNC, T&&>> fmap(FUNC func, shared_task<T > task);
265+
266+ // Call func() after task completes, returning a task containing the
267+ // result of func().
268+ template<typename FUNC >
269+ task< std::invoke_result_t<FUNC > > fmap(FUNC func, shared_task<void > task);
246270}
247271```
248272
@@ -364,7 +388,12 @@ namespace cppcoro
364388 };
365389
366390 template<typename T>
367- void swap(generator<T>& a, generator<T>&b) noexcept;
391+ void swap(generator<T>& a, generator<T>& b) noexcept;
392+
393+ // Apply function, func, lazily to each element of the source generator
394+ // and yield a sequence of the results of calls to func().
395+ template<typename FUNC, typename T>
396+ generator<std::invoke_result_t<FUNC, T&>> fmap(FUNC func, generator<T> source);
368397}
369398```
370399
@@ -401,6 +430,10 @@ cppcoro::recursive_generator<dir_entry> list_directory_recursive(std::filesystem
401430}
402431```
403432
433+ Note that applying the ` fmap() ` operator to a ` recursive_generator<T> ` will yield a ` generator<U> `
434+ type rather than a ` recursive_generator<U> ` . This is because uses of ` fmap ` are generally not used
435+ in recursive contexts and we try to avoid the extra overhead incurred by ` recursive_generator ` .
436+
404437## ` async_generator<T> `
405438
406439An ` async_generator ` represents a coroutine type that produces a sequence of values of type, ` T ` , where values are produced lazily and values may be produced asynchronously.
@@ -491,6 +524,14 @@ namespace cppcoro
491524 iterator end() noexcept;
492525
493526 };
527+
528+ template<typename T>
529+ void swap(async_generator<T>& a, async_generator<T>& b);
530+
531+ // Apply 'func' to each element of the source generator, yielding a sequence of
532+ // the results of calling 'func' on the source elements.
533+ template<typename FUNC, typename T>
534+ async_generator<std::invoke_result_t<FUNC, T&>> fmap(FUNC func, async_generator<T> source);
494535}
495536```
496537
@@ -1293,6 +1334,7 @@ namespace cppcoro
12931334All ` open() ` functions throw ` std::system_error ` on failure.
12941335
12951336# Functions
1337+
12961338## ` sync_wait() `
12971339
12981340The ` sync_wait() ` function can be used to synchronously wait until the specified ` task `
@@ -1529,6 +1571,73 @@ task<> example2()
15291571}
15301572```
15311573
1574+ ## `fmap()`
1575+
1576+ The `fmap()` function can be used to apply a callable function to the value(s) contained within
1577+ a container-type, returning a new container-type of the results of applying the function the
1578+ contained value(s).
1579+
1580+ The `fmap()` function can apply a function to values of type `task<T>`, `shared_task<T>`, `generator<T>`,
1581+ `recursive_generator<T>` and `async_generator<T>`.
1582+
1583+ Each of these types provides an overload for `fmap()` that takes two arguments; a function to apply and the container value.
1584+ See documentation for each type for the supported `fmap()` overloads.
1585+
1586+ For example, the `fmap()` function can be used to apply a function to the eventual result of
1587+ a `task<T>`, producing a new `task<U>` that will complete with the return-value of the function.
1588+ ```c++
1589+ // Given a function you want to apply that converts
1590+ // a value of type A to value of type B.
1591+ B a_to_b(A value);
1592+
1593+ // And a task that yields a value of type A
1594+ cppcoro::task<A> get_an_a();
1595+
1596+ // We can apply the function to the result of the task using fmap()
1597+ // and obtain a new task yielding the result.
1598+ cppcoro::task<B> bTask = fmap(a_to_b, get_an_a());
1599+
1600+ // An alternative syntax is to use the pipe notation.
1601+ cppcoro::task<B> bTask = get_an_a() | cppcoro::fmap(a_to_b);
1602+ ```
1603+
1604+ API Summary:
1605+ ``` c++
1606+ // <cppcoro/fmap.hpp>
1607+ namespace cppcoro
1608+ {
1609+ template<typename FUNC >
1610+ struct fmap_transform
1611+ {
1612+ fmap_transform(FUNC&& func) noexcept(std::is_nothrow_move_constructible_v<FUNC >);
1613+ FUNC func;
1614+ };
1615+
1616+ // Type-deducing constructor for fmap_transform object that can be used
1617+ // in conjunction with operator|.
1618+ template<typename FUNC >
1619+ fmap_transform<FUNC > fmap(FUNC&& func);
1620+
1621+ // operator| overloads for providing pipe-based syntactic sugar for fmap()
1622+ // such that the expression:
1623+ // <value-expr > | cppcoro::fmap(<func-expr >)
1624+ // is equivalent to:
1625+ // fmap(<func-expr >, <value-expr >)
1626+
1627+ template<typename T, typename FUNC>
1628+ decltype (auto) operator|(T&& value, fmap_transform<FUNC >&& transform);
1629+
1630+ template<typename T, typename FUNC>
1631+ decltype(auto) operator|(T&& value, fmap_transform<FUNC >& transform);
1632+
1633+ template<typename T, typename FUNC>
1634+ decltype(auto) operator|(T&& value, const fmap_transform<FUNC >& transform);
1635+ }
1636+ ```
1637+
1638+ The `fmap()` function is designed to look up the correct overload by argument-dependent
1639+ lookup (ADL) so it should generally be called without the `cppcoro::` prefix.
1640+
15321641# Concepts
15331642
15341643## `Scheduler` concept
0 commit comments