Lumiera
0.pre.03
»edit your freedom«
|
Go to the source code of this file.
Building tree expanding and backtracking evaluations within hierarchical scopes.
Based on the Lumiera Forward Iterator concept and using the basic IterAdapter templates, these components allow to implement typical evaluation strategies, like conditional expanding or depth-first exploration of a hierarchical structure. Since the access to this structure is abstracted through the underlying iterator, what we effectively get is a functional datastructure. The implementation is based on the idea of a "state core", which is wrapped right into the iterator itself (value semantics) – similar to the IterStateWrapper, which is one of the basic helper templates provided by iter-adapter.hpp.
The fundamental idea behind the implementation technique used here is the Monad pattern known from functional programming. A Monad is a container holding some arbitrarily typed base value; the monad can be seen as „amplifying“ and enhancing the contained base value by attaching additional properties or capabilities This is a rather detached concept with a wide variety of applications (things like IO state, parsers, combinators, calculations with exception handling but also simple data structures like lists or trees). The key point with any monad is the ability to bind a function into the monad; this function will work on the contained base values and produce a modified new monad instance. In the simple case of a list, "binding" a function basically means to map the function onto the elements in the list. (strictly speaking, it means the flatMap
operation)
Based on such concepts, structures and evaluation patterns, the IterExplorer serves the purpose to provide building blocks to assemble a processing pipeline, where processing will happen on demand, while iterating. IterExplorer itself is both a Lumiera Forward Iterator based on some wrapped data source, and at the same time it is a builder to chain up processing steps to work on the data pulled from that source. These processing steps are attached as decorators wrapping the source, in the order the corresponding builder functions were invoked.
expandChildren()
call on the processing pipeline. Thus, binding the expansion functor has augmented the data source with the ability to explore some part in more detail when required.In itself, the IterExplorer is an iterator with implementation defined type (all operations being inlined). But it is possible to package this structure behind a conventional iteration interface with virtual functions. By invoking the terminal builder function IterExplorer::asIterSource(), the iterator compound type, as created thus far, will be moved into a heap allocation, returning a front-end based on IterSource. In addition, the actually returned type, IterExplorerSource, exposes the expandChildren()
operation as discussed above.
This design leads to a complete separation of the transforming operation from the mechanics how to apply that operation and combine the results. More specifically, we rely on an iterator to represent an abstracted source of data and we expose the combined and transformed results again as such an abstracted data sequence. Thereby, a trend towards separation of concerns is introduced; the data source remains opaque, while the manipulation to apply can be selected at runtime or written inline as Lambda. The iterator itself is a self-contained value and represents partial evaluation state without requiring a container for intermediary results.
The builder operations assemble a heavily nested type, each builder call thereby adding yet another layer of subclassing. The templates involved into this build process are specialised, as driven by the actual functor type bound into each builder step; this functor is investigated and possibly adapted, according to its input type, while its output type determines the value type used in the pipeline "downstream". A functor with an input (argument) type incompatible or unsuitable to the existing pipeline will produce that endless sway of template error messages we all love so much. When this happens, please look at the static assertion error message typically to be found below the first template-instantiation stack sequence of messages.
Definition in file iter-explorer.hpp.
#include "lib/error.hpp"
#include "lib/uninitialised-storage.hpp"
#include "lib/meta/duck-detector.hpp"
#include "lib/meta/function.hpp"
#include "lib/meta/trait.hpp"
#include "lib/wrapper.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/iter-source.hpp"
#include "lib/iter-stack.hpp"
#include "lib/util.hpp"
#include <functional>
#include <optional>
#include <utility>
Functions | |
template<class IT > | |
auto | explore (IT &&srcSeq) |
start building a IterExplorer by suitably wrapping the given iterable source. More... | |
template<typename FUN , typename SRC > | |
void | static_assert_isPredicate () |
Variables | |
constexpr auto | ACCEPT_ALL = [](auto){return true;} |
constexpr auto | IDENTITY = [](auto it){return *it;} |
Namespaces | |
lib | |
Implementation namespace for support and library code. | |
struct lib::anonymous_namespace{iter-explorer.hpp}::_DecoratorTraits |
struct lib::anonymous_namespace{iter-explorer.hpp}::_DecoratorTraits< ISO *, enable_if< is_base_of< IterSource< typename ISO::value_type >, ISO > > > |
struct lib::anonymous_namespace{iter-explorer.hpp}::_DecoratorTraits< SRC, enable_if< is_StateCore< SRC > > > |
Class Members | ||
---|---|---|
typedef typename Strip< SRC >::Type | SrcRaw | |
typedef typename CoreYield < SrcRaw >::value_type |
SrcVal | |
typedef IterableDecorator < SrcVal, CheckedCore< SrcRaw > > |
SrcIter |
struct lib::anonymous_namespace{iter-explorer.hpp}::_DecoratorTraits< SRC, enable_if< shall_use_Lumiera_Iter< SRC > > > |
struct lib::anonymous_namespace{iter-explorer.hpp}::_DecoratorTraits< SRC, enable_if< shall_wrap_STL_Iter< SRC > > > |
struct lib::anonymous_namespace{iter-explorer.hpp}::CoreYield |
struct lib::iter_explorer::_FunTraits::FunDetector |
Class Members | ||
---|---|---|
typedef typename _Fun< F >::Sig | Sig |