stabilize build system: depends, installer, boost/bdb fixes, cross targets groundwork
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Applicative`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_APPLICATIVE_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_APPLICATIVE_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Applicative Applicative
|
||||
//! The `Applicative` concept represents `Functor`s with the ability
|
||||
//! to lift values and combine computations.
|
||||
//!
|
||||
//! A `Functor` can only take a normal function and map it over a
|
||||
//! structure containing values to obtain a new structure containing
|
||||
//! values. Intuitively, an `Applicative` can also take a value and
|
||||
//! lift it into the structure. In addition, an `Applicative` can take
|
||||
//! a structure containing functions and apply it to a structure
|
||||
//! containing values to obtain a new structure containing values.
|
||||
//! By currying the function(s) inside the structure, it is then
|
||||
//! also possible to apply n-ary functions to n structures containing
|
||||
//! values.
|
||||
//!
|
||||
//! @note
|
||||
//! This documentation does not go into much details about the nature
|
||||
//! of applicatives. However, the [Typeclassopedia][1] is a nice
|
||||
//! Haskell-oriented resource where such information can be found.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `lift` and `ap` satisfying the laws below. An `Applicative` must
|
||||
//! also be a `Functor`.
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! Given an `Applicative` `F`, the following laws must be satisfied:
|
||||
//! 1. Identity\n
|
||||
//! For all objects `xs` of tag `F(A)`,
|
||||
//! @code
|
||||
//! ap(lift<F>(id), xs) == xs
|
||||
//! @endcode
|
||||
//!
|
||||
//! 2. Composition\n
|
||||
//! For all objects `xs` of tag `F(A)` and functions-in-an-applicative
|
||||
//! @f$ fs : F(B \to C) @f$,
|
||||
//! @f$ gs : F(A \to B) @f$,
|
||||
//! @code
|
||||
//! ap(ap(lift<F>(compose), fs, gs), xs) == ap(fs, ap(gs, xs))
|
||||
//! @endcode
|
||||
//!
|
||||
//! 3. Homomorphism\n
|
||||
//! For all objects `x` of tag `A` and functions @f$ f : A \to B @f$,
|
||||
//! @code
|
||||
//! ap(lift<F>(f), lift<F>(x)) == lift<F>(f(x))
|
||||
//! @endcode
|
||||
//!
|
||||
//! 4. Interchange\n
|
||||
//! For all objects `x` of tag `A` and functions-in-an-applicative
|
||||
//! @f$ fs : F(A \to B) @f$,
|
||||
//! @code
|
||||
//! ap(fs, lift<F>(x)) == ap(lift<F>(apply(-, x)), fs)
|
||||
//! @endcode
|
||||
//! where `apply(-, x)` denotes the partial application of the `apply`
|
||||
//! function from the @ref group-functional module to the `x` argument.
|
||||
//!
|
||||
//! As a consequence of these laws, the model of `Functor` for `F` will
|
||||
//! satisfy the following for all objects `xs` of tag `F(A)` and functions
|
||||
//! @f$ f : A \to B @f$:
|
||||
//! @code
|
||||
//! transform(xs, f) == ap(lift<F>(f), xs)
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Refined concept
|
||||
//! ---------------
|
||||
//! 1. `Functor` (free model)\n
|
||||
//! As a consequence of the laws, any `Applicative F` can be made a
|
||||
//! `Functor` by setting
|
||||
//! @code
|
||||
//! transform(xs, f) = ap(lift<F>(f), xs)
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::lazy`, `hana::optional`, `hana::tuple`
|
||||
//!
|
||||
//!
|
||||
//! @anchor applicative-transformation
|
||||
//! Structure-preserving functions
|
||||
//! ------------------------------
|
||||
//! An _applicative transformation_ is a function @f$ t : F(X) \to G(X) @f$
|
||||
//! between two Applicatives `F` and `G`, where `X` can be any tag, and
|
||||
//! which preserves the operations of an Applicative. In other words, for
|
||||
//! all objects `x` of tag `X`, functions-in-an-applicative
|
||||
//! @f$ fs : F(X \to Y) @f$ and objects `xs` of tag `F(X)`,
|
||||
//! @code
|
||||
//! t(lift<F>(x)) == lift<G>(x)
|
||||
//! t(ap(fs, xs)) == ap(t(fs), t(xs))
|
||||
//! @endcode
|
||||
//!
|
||||
//! [1]: https://wiki.haskell.org/Typeclassopedia#Applicative
|
||||
template <typename A>
|
||||
struct Applicative;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_APPLICATIVE_HPP
|
||||
@@ -0,0 +1,111 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Comonad`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_COMONAD_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_COMONAD_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
// Note: We use a multiline C++ comment because there's a double backslash
|
||||
// symbol in the documentation (for LaTeX), which triggers
|
||||
// warning: multi-line comment [-Wcomment]
|
||||
// on GCC.
|
||||
|
||||
/*!
|
||||
@ingroup group-concepts
|
||||
@defgroup group-Comonad Comonad
|
||||
The `Comonad` concept represents context-sensitive computations and
|
||||
data.
|
||||
|
||||
Formally, the Comonad concept is dual to the Monad concept.
|
||||
But unless you're a mathematician, you don't care about that and it's
|
||||
fine. So intuitively, a Comonad represents context sensitive values
|
||||
and computations. First, Comonads make it possible to extract
|
||||
context-sensitive values from their context with `extract`.
|
||||
In contrast, Monads make it possible to wrap raw values into
|
||||
a given context with `lift` (from Applicative).
|
||||
|
||||
Secondly, Comonads make it possible to apply context-sensitive values
|
||||
to functions accepting those, and to return the result as a
|
||||
context-sensitive value using `extend`. In contrast, Monads make
|
||||
it possible to apply a monadic value to a function accepting a normal
|
||||
value and returning a monadic value, and to return the result as a
|
||||
monadic value (with `chain`).
|
||||
|
||||
Finally, Comonads make it possible to wrap a context-sensitive value
|
||||
into an extra layer of context using `duplicate`, while Monads make
|
||||
it possible to take a value with an extra layer of context and to
|
||||
strip it with `flatten`.
|
||||
|
||||
Whereas `lift`, `chain` and `flatten` from Applicative and Monad have
|
||||
signatures
|
||||
\f{align*}{
|
||||
\mathtt{lift}_M &: T \to M(T) \\
|
||||
\mathtt{chain} &: M(T) \times (T \to M(U)) \to M(U) \\
|
||||
\mathtt{flatten} &: M(M(T)) \to M(T)
|
||||
\f}
|
||||
|
||||
`extract`, `extend` and `duplicate` from Comonad have signatures
|
||||
\f{align*}{
|
||||
\mathtt{extract} &: W(T) \to T \\
|
||||
\mathtt{extend} &: W(T) \times (W(T) \to U) \to W(U) \\
|
||||
\mathtt{duplicate} &: W(T) \to W(W(T))
|
||||
\f}
|
||||
|
||||
Notice how the "arrows" are reversed. This symmetry is essentially
|
||||
what we mean by Comonad being the _dual_ of Monad.
|
||||
|
||||
@note
|
||||
The [Typeclassopedia][1] is a nice Haskell-oriented resource for further
|
||||
reading about Comonads.
|
||||
|
||||
|
||||
Minimal complete definition
|
||||
---------------------------
|
||||
`extract` and (`extend` or `duplicate`) satisfying the laws below.
|
||||
A `Comonad` must also be a `Functor`.
|
||||
|
||||
|
||||
Laws
|
||||
----
|
||||
For all Comonads `w`, the following laws must be satisfied:
|
||||
@code
|
||||
extract(duplicate(w)) == w
|
||||
transform(duplicate(w), extract) == w
|
||||
duplicate(duplicate(w)) == transform(duplicate(w), duplicate)
|
||||
@endcode
|
||||
|
||||
@note
|
||||
There are several equivalent ways of defining Comonads, and this one
|
||||
is just one that was picked arbitrarily for simplicity.
|
||||
|
||||
|
||||
Refined concept
|
||||
---------------
|
||||
1. Functor\n
|
||||
Every Comonad is also required to be a Functor. At first, one might think
|
||||
that it should instead be some imaginary concept CoFunctor. However, it
|
||||
turns out that a CoFunctor is the same as a `Functor`, hence the
|
||||
requirement that a `Comonad` also is a `Functor`.
|
||||
|
||||
|
||||
Concrete models
|
||||
---------------
|
||||
`hana::lazy`
|
||||
|
||||
[1]: https://wiki.haskell.org/Typeclassopedia#Comonad
|
||||
|
||||
*/
|
||||
template <typename W>
|
||||
struct Comonad;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_COMONAD_HPP
|
||||
@@ -0,0 +1,160 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Comparable`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_COMPARABLE_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_COMPARABLE_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Comparable Comparable
|
||||
//! The `Comparable` concept defines equality and inequality.
|
||||
//!
|
||||
//! Intuitively, `Comparable` objects must define a binary predicate named
|
||||
//! `equal` that returns whether both objects represent the same abstract
|
||||
//! value. In other words, `equal` must check for deep equality. Since
|
||||
//! "representing the same abstract value" is difficult to express
|
||||
//! formally, the exact meaning of equality is partially left to
|
||||
//! interpretation by the programmer with the following guidelines:\n
|
||||
//! 1. Equality should be compatible with copy construction; copy
|
||||
//! constructing a value yields an `equal` value.
|
||||
//! 2. Equality should be independent of representation; an object
|
||||
//! representing a fraction as `4/8` should be `equal` to an object
|
||||
//! representing a fraction as `2/4`, because they both represent
|
||||
//! the mathematical object `1/2`.
|
||||
//!
|
||||
//! Moreover, `equal` must exhibit properties that make it intuitive to
|
||||
//! use for determining the equivalence of objects, which is formalized
|
||||
//! by the laws for `Comparable`.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! 1. `equal`\n
|
||||
//! When `equal` is defined, `not_equal` is implemented by default as its
|
||||
//! complement. For all objects `x`, `y` of a `Comparable` tag,
|
||||
//! @code
|
||||
//! not_equal(x, y) == not_(equal(x, y))
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! `equal` must define an [equivalence relation][1], and `not_equal` must
|
||||
//! be its complement. In other words, for all objects `a`, `b`, `c` with
|
||||
//! a `Comparable` tag, the following must hold:
|
||||
//! @code
|
||||
//! equal(a, a) // Reflexivity
|
||||
//! if equal(a, b) then equal(b, a) // Symmetry
|
||||
//! if equal(a, b) && equal(b, c) then equal(a, c) // Transitivity
|
||||
//! not_equal(a, b) is equivalent to not_(equal(a, b))
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`, `hana::map`, `hana::optional`, `hana::pair`,
|
||||
//! `hana::range`, `hana::set`, `hana::string`, `hana::tuple`,
|
||||
//! `hana::type`
|
||||
//!
|
||||
//!
|
||||
//! Free model for `EqualityComparable` data types
|
||||
//! ----------------------------------------------
|
||||
//! Two data types `T` and `U` that model the cross-type EqualityComparable
|
||||
//! concept presented in [N3351][2] automatically model the `Comparable`
|
||||
//! concept by setting
|
||||
//! @code
|
||||
//! equal(x, y) = (x == y)
|
||||
//! @endcode
|
||||
//! Note that this also makes EqualityComparable types in the
|
||||
//! [usual sense][3] models of `Comparable` in the same way.
|
||||
//!
|
||||
//!
|
||||
//! Equality-preserving functions
|
||||
//! -----------------------------
|
||||
//! Let `A` and `B` be two `Comparable` tags. A function @f$f : A \to B@f$
|
||||
//! is said to be equality-preserving if it preserves the structure of the
|
||||
//! `Comparable` concept, which can be rigorously stated as follows. For
|
||||
//! all objects `x`, `y` of tag `A`,
|
||||
//! @code
|
||||
//! if equal(x, y) then equal(f(x), f(y))
|
||||
//! @endcode
|
||||
//! Equivalently, we simply require that `f` is a function in the usual
|
||||
//! mathematical sense. Another property is [injectivity][4], which can be
|
||||
//! viewed as being a "lossless" mapping. This property can be stated as
|
||||
//! @code
|
||||
//! if equal(f(x), f(y)) then equal(x, y)
|
||||
//! @endcode
|
||||
//! This is equivalent to saying that `f` maps distinct elements to
|
||||
//! distinct elements, hence the "lossless" analogy. In other words, `f`
|
||||
//! will not collapse distinct elements from its domain into a single
|
||||
//! element in its image, thus losing information.
|
||||
//!
|
||||
//! These functions are very important, especially equality-preserving
|
||||
//! ones, because they allow us to reason simply about programs. Also
|
||||
//! note that the property of being equality-preserving is taken for
|
||||
//! granted in mathematics because it is part of the definition of a
|
||||
//! function. We feel it is important to make the distinction here
|
||||
//! because programming has evolved differently and as a result
|
||||
//! programmers are used to work with functions that do not preserve
|
||||
//! equality.
|
||||
//!
|
||||
//!
|
||||
//! Cross-type version of the methods
|
||||
//! ---------------------------------
|
||||
//! The `equal` and `not_equal` methods are "overloaded" to handle
|
||||
//! distinct tags with certain properties. Specifically, they are
|
||||
//! defined for _distinct_ tags `A` and `B` such that
|
||||
//! 1. `A` and `B` share a common tag `C`, as determined by the
|
||||
//! `common` metafunction
|
||||
//! 2. `A`, `B` and `C` are all `Comparable` when taken individually
|
||||
//! 3. @f$ \mathtt{to<C>} : A \to C @f$ and @f$\mathtt{to<C>} : B \to C@f$
|
||||
//! are both equality-preserving and injective (i.e. they are embeddings),
|
||||
//! as determined by the `is_embedding` metafunction.
|
||||
//!
|
||||
//! The method definitions for tags satisfying the above properties are
|
||||
//! @code
|
||||
//! equal(x, y) = equal(to<C>(x), to<C>(y))
|
||||
//! not_equal(x, y) = not_equal(to<C>(x), to<C>(y))
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Important note: special behavior of `equal`
|
||||
//! -------------------------------------------
|
||||
//! In the context of programming with heterogeneous values, it is useful
|
||||
//! to have unrelated objects compare `false` instead of triggering an
|
||||
//! error. For this reason, `equal` adopts a special behavior for
|
||||
//! unrelated objects of tags `T` and `U` that do not satisfy the above
|
||||
//! requirements for the cross-type overloads. Specifically, when `T` and
|
||||
//! `U` are unrelated (i.e. `T` can't be converted to `U` and vice-versa),
|
||||
//! comparing objects with those tags yields a compile-time false value.
|
||||
//! This has the effect that unrelated objects like `float` and
|
||||
//! `std::string` will compare false, while comparing related objects that
|
||||
//! can not be safely embedded into the same super structure (like
|
||||
//! `long long` and `float` because of the precision loss) will trigger a
|
||||
//! compile-time assertion. Also note that for any tag `T` for which the
|
||||
//! minimal complete definition of `Comparable` is not provided, a
|
||||
//! compile-time assertion will also be triggered because `T` and `T`
|
||||
//! trivially share the common tag `T`, which is the expected behavior.
|
||||
//! This design choice aims to provide more flexibility for comparing
|
||||
//! objects, while still rejecting usage patterns that are most likely
|
||||
//! programming errors.
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Equivalence_relation#Definition
|
||||
//! [2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf
|
||||
//! [3]: http://en.cppreference.com/w/cpp/concept/EqualityComparable
|
||||
//! [4]: http://en.wikipedia.org/wiki/Injective_function
|
||||
template <typename T>
|
||||
struct Comparable;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_COMPARABLE_HPP
|
||||
@@ -0,0 +1,210 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Constant`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_CONSTANT_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_CONSTANT_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Constant Constant
|
||||
//! The `Constant` concept represents data that can be manipulated at
|
||||
//! compile-time.
|
||||
//!
|
||||
//! At its core, `Constant` is simply a generalization of the principle
|
||||
//! behind `std::integral_constant` to all types that can be constructed
|
||||
//! at compile-time, i.e. to all types with a `constexpr` constructor
|
||||
//! (also called [Literal types][1]). More specifically, a `Constant` is
|
||||
//! an object from which a `constexpr` value may be obtained (through the
|
||||
//! `value` method) regardless of the `constexpr`ness of the object itself.
|
||||
//!
|
||||
//! All `Constant`s must be somewhat equivalent, in the following sense.
|
||||
//! Let `C(T)` and `D(U)` denote the tags of `Constant`s holding objects
|
||||
//! of type `T` and `U`, respectively. Then, an object with tag `D(U)`
|
||||
//! must be convertible to an object with tag `C(T)` whenever `U` is
|
||||
//! convertible to `T`, as determined by `is_convertible`. The
|
||||
//! interpretation here is that a `Constant` is just a box holding
|
||||
//! an object of some type, and it should be possible to swap between
|
||||
//! boxes whenever the objects inside the boxes can be swapped.
|
||||
//!
|
||||
//! Because of this last requirement, one could be tempted to think that
|
||||
//! specialized "boxes" like `std::integral_constant` are prevented from
|
||||
//! being `Constant`s because they are not able to hold objects of any
|
||||
//! type `T` (`std::integral_constant` may only hold integral types).
|
||||
//! This is false; the requirement should be interpreted as saying that
|
||||
//! whenever `C(T)` is _meaningful_ (e.g. only when `T` is integral for
|
||||
//! `std::integral_constant`) _and_ there exists a conversion from `U`
|
||||
//! to `T`, then a conversion from `D(U)` to `C(T)` should also exist.
|
||||
//! The precise requirements for being a `Constant` are embodied in the
|
||||
//! following laws.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `value` and `to`, satisfying the laws below.
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! Let `c` be an object of with tag `C`, which represents a `Constant`
|
||||
//! holding an object with tag `T`. The first law ensures that the value
|
||||
//! of the wrapped object is always a constant expression by requiring
|
||||
//! the following to be well-formed:
|
||||
//! @code
|
||||
//! constexpr auto x = hana::value<decltype(c)>();
|
||||
//! @endcode
|
||||
//!
|
||||
//! This means that the `value` function must return an object that can
|
||||
//! be constructed at compile-time. It is important to note how `value`
|
||||
//! only receives the type of the object and not the object itself.
|
||||
//! This is the core of the `Constant` concept; it means that the only
|
||||
//! information required to implement `value` must be stored in the _type_
|
||||
//! of its argument, and hence be available statically.
|
||||
//!
|
||||
//! The second law that must be satisfied ensures that `Constant`s are
|
||||
//! basically dumb boxes, which makes it possible to provide models for
|
||||
//! many concepts without much work from the user. The law simply asks
|
||||
//! for the following expression to be valid:
|
||||
//! @code
|
||||
//! to<C>(i)
|
||||
//! @endcode
|
||||
//! where, `i` is an _arbitrary_ `Constant` holding an internal value
|
||||
//! with a tag that can be converted to `T`, as determined by the
|
||||
//! `hana::is_convertible` metafunction. In other words, whenever `U` is
|
||||
//! convertible to `T`, a `Constant` holding a `U` is convertible to
|
||||
//! a `Constant` holding a `T`, if such a `Constant` can be created.
|
||||
//!
|
||||
//! Finally, the tag `C` must provide a nested `value_type` alias to `T`,
|
||||
//! which allows us to query the tag of the inner value held by objects
|
||||
//! with tag `C`. In other words, the following must be true for any
|
||||
//! object `c` with tag `C`:
|
||||
//! @code
|
||||
//! std::is_same<
|
||||
//! C::value_type,
|
||||
//! tag_of<decltype(hana::value(c))>::type
|
||||
//! >::value
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! In certain cases, a `Constant` can automatically be made a model of
|
||||
//! another concept. In particular, if a `Constant` `C` is holding an
|
||||
//! object of tag `T`, and if `T` models a concept `X`, then `C` may
|
||||
//! in most cases model `X` by simply performing whatever operation is
|
||||
//! required on its underlying value, and then wrapping the result back
|
||||
//! in a `C`.
|
||||
//!
|
||||
//! More specifically, if a `Constant` `C` has an underlying value
|
||||
//! (`C::value_type`) which is a model of `Comparable`, `Orderable`,
|
||||
//! `Logical`, or `Monoid` up to `EuclideanRing`, then `C` must also
|
||||
//! be a model of those concepts. In other words, when `C::value_type`
|
||||
//! models one of the listed concepts, `C` itself must also model that
|
||||
//! concept. However, note that free models are provided for all of
|
||||
//! those concepts, so no additional work must be done.
|
||||
//!
|
||||
//! While it would be possible in theory to provide models for concepts
|
||||
//! like `Foldable` too, only a couple of concepts are useful to have as
|
||||
//! `Constant` in practice. Providing free models for the concepts listed
|
||||
//! above is useful because it allows various types of integral constants
|
||||
//! (`std::integral_constant`, `mpl::integral_c`, etc...) to easily have
|
||||
//! models for them just by defining the `Constant` concept.
|
||||
//!
|
||||
//! @remark
|
||||
//! An interesting observation is that `Constant` is actually the
|
||||
//! canonical embedding of the subcategory of `constexpr` things
|
||||
//! into the Hana category, which contains everything in this library.
|
||||
//! Hence, whatever is true in that subcategory is also true here, via
|
||||
//! this functor. This is why we can provide models of any concept that
|
||||
//! works on `constexpr` things for Constants, by simply passing them
|
||||
//! through that embedding.
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`
|
||||
//!
|
||||
//!
|
||||
//! Provided conversion to the tag of the underlying value
|
||||
//! ------------------------------------------------------
|
||||
//! Any `Constant` `c` holding an underlying value of tag `T` is
|
||||
//! convertible to any tag `U` such that `T` is convertible to `U`.
|
||||
//! Specifically, the conversion is equivalent to
|
||||
//! @code
|
||||
//! to<U>(c) == to<U>(value<decltype(c)>())
|
||||
//! @endcode
|
||||
//!
|
||||
//! Also, those conversions are marked as an embedding whenever the
|
||||
//! conversion of underlying types is an embedding. This is to allow
|
||||
//! Constants to inter-operate with `constexpr` objects easily:
|
||||
//! @code
|
||||
//! plus(int_c<1>, 1) == 2
|
||||
//! @endcode
|
||||
//!
|
||||
//! Strictly speaking, __this is sometimes a violation__ of what it means
|
||||
//! to be an embedding. Indeed, while there exists an embedding from any
|
||||
//! Constant to a `constexpr` object (since Constant is just the canonical
|
||||
//! inclusion), there is no embedding from a Constant to a runtime
|
||||
//! object since we would lose the ability to define the `value` method
|
||||
//! (the `constexpr`ness of the object would have been lost). Since there
|
||||
//! is no way to distinguish `constexpr` and non-`constexpr` objects based
|
||||
//! on their type, Hana has no way to know whether the conversion is to a
|
||||
//! `constexpr` object of not. In other words, the `to` method has no way
|
||||
//! to differentiate between
|
||||
//! @code
|
||||
//! constexpr int i = hana::to<int>(int_c<1>);
|
||||
//! @endcode
|
||||
//! which is an embedding, and
|
||||
//! @code
|
||||
//! int i = hana::to<int>(int_c<1>);
|
||||
//! @endcode
|
||||
//!
|
||||
//! which isn't. To be on the safer side, we could mark the conversion
|
||||
//! as not-an-embedding. However, if e.g. the conversion from
|
||||
//! `integral_constant_tag<int>` to `int` was not marked as an embedding,
|
||||
//! we would have to write `plus(to<int>(int_c<1>), 1)` instead of just
|
||||
//! `plus(int_c<1>, 1)`, which is cumbersome. Hence, the conversion is
|
||||
//! marked as an embedding, but this also means that code like
|
||||
//! @code
|
||||
//! int i = 1;
|
||||
//! plus(int_c<1>, i);
|
||||
//! @endcode
|
||||
//! will be considered valid, which implicitly loses the fact that
|
||||
//! `int_c<1>` is a Constant, and hence does not follow the usual rules
|
||||
//! for cross-type operations in Hana.
|
||||
//!
|
||||
//!
|
||||
//! Provided common data type
|
||||
//! -------------------------
|
||||
//! Because of the requirement that `Constant`s be interchangeable when
|
||||
//! their contents are compatible, two `Constant`s `A` and `B` will have
|
||||
//! a common data type whenever `A::value_type` and `B::value_type` have
|
||||
//! one. Their common data type is an unspecified `Constant` `C` such
|
||||
//! that `C::value_type` is exactly `common_t<A::value_type, B::value_type>`.
|
||||
//! A specialization of the `common` metafunction is provided for
|
||||
//! `Constant`s to reflect this.
|
||||
//!
|
||||
//! In the same vein, a common data type is also provided from any
|
||||
//! constant `A` to a type `T` such that `A::value_type` and `T` share
|
||||
//! a common type. The common type between `A` and `T` is obviously the
|
||||
//! common type between `A::value_type` and `T`. As explained above in
|
||||
//! the section on conversions, this is sometimes a violation of the
|
||||
//! definition of a common type, because there must be an embedding
|
||||
//! to the common type, which is not always the case. For the same
|
||||
//! reasons as explained above, this common type is still provided.
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.cppreference.com/w/cpp/concept/LiteralType
|
||||
template <typename C>
|
||||
struct Constant;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_CONSTANT_HPP
|
||||
@@ -0,0 +1,117 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::EuclideanRing`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_EUCLIDEAN_RING_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_EUCLIDEAN_RING_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-EuclideanRing Euclidean Ring
|
||||
//! The `EuclideanRing` concept represents a commutative `Ring` that
|
||||
//! can also be endowed with a division algorithm.
|
||||
//!
|
||||
//! A Ring defines a binary operation often called _multiplication_ that
|
||||
//! can be used to combine two elements of the ring into a new element of
|
||||
//! the ring. An [Euclidean ring][1], also called an Euclidean domain, adds
|
||||
//! the ability to define a special function that generalizes the Euclidean
|
||||
//! division of integers.
|
||||
//!
|
||||
//! However, an Euclidean ring must also satisfy one more property, which
|
||||
//! is that of having no non-zero zero divisors. In a Ring `(R, +, *)`, it
|
||||
//! follows quite easily from the axioms that `x * 0 == 0` for any ring
|
||||
//! element `x`. However, there is nothing that mandates `0` to be the
|
||||
//! only ring element sending other elements to `0`. Hence, in some Rings,
|
||||
//! it is possible to have elements `x` and `y` such that `x * y == 0`
|
||||
//! while not having `x == 0` nor `y == 0`. We call these elements divisors
|
||||
//! of zero, or zero divisors. For example, this situation arises in the
|
||||
//! Ring of integers modulo 4 (the set `{0, 1, 2, 3}`) with addition and
|
||||
//! multiplication `mod 4` as binary operations. In this case, we have that
|
||||
//! @code
|
||||
//! 2 * 2 == 4
|
||||
//! == 0 (mod 4)
|
||||
//! @endcode
|
||||
//! even though `2 != 0 (mod 4)`.
|
||||
//!
|
||||
//! Following this line of thought, an Euclidean ring requires its only
|
||||
//! zero divisor is zero itself. In other words, the multiplication in an
|
||||
//! Euclidean won't send two non-zero elements to zero. Also note that
|
||||
//! since multiplication in a `Ring` is not necessarily commutative, it
|
||||
//! is not always the case that
|
||||
//! @code
|
||||
//! x * y == 0 implies y * x == 0
|
||||
//! @endcode
|
||||
//! To be rigorous, we should then distinguish between elements that are
|
||||
//! zero divisors when multiplied to the right and to the left.
|
||||
//! Fortunately, the concept of an Euclidean ring requires the Ring
|
||||
//! multiplication to be commutative. Hence,
|
||||
//! @code
|
||||
//! x * y == y * x
|
||||
//! @endcode
|
||||
//! and we do not have to distinguish between left and right zero divisors.
|
||||
//!
|
||||
//! Typical examples of Euclidean rings include integers and polynomials
|
||||
//! over a field. The method names used here refer to the Euclidean ring
|
||||
//! of integers under the usual addition, multiplication and division
|
||||
//! operations.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `div` and `mod` satisfying the laws below
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! To simplify the reading, we will use the `+`, `*`, `/` and `%`
|
||||
//! operators with infix notation to denote the application of the
|
||||
//! corresponding methods in Monoid, Group, Ring and EuclideanRing.
|
||||
//! For all objects `a` and `b` of an `EuclideanRing` `R`, the
|
||||
//! following laws must be satisfied:
|
||||
//! @code
|
||||
//! a * b == b * a // commutativity
|
||||
//! (a / b) * b + a % b == a if b is non-zero
|
||||
//! zero<R>() % b == zero<R>() if b is non-zero
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! `Monoid`, `Group`, `Ring`
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`
|
||||
//!
|
||||
//!
|
||||
//! Free model for non-boolean integral data types
|
||||
//! ----------------------------------------------
|
||||
//! A data type `T` is integral if `std::is_integral<T>::%value` is true.
|
||||
//! For a non-boolean integral data type `T`, a model of `EuclideanRing`
|
||||
//! is automatically defined by using the `Ring` model provided for
|
||||
//! arithmetic data types and setting
|
||||
//! @code
|
||||
//! div(x, y) = (x / y)
|
||||
//! mod(x, y) = (x % y)
|
||||
//! @endcode
|
||||
//!
|
||||
//! @note
|
||||
//! The rationale for not providing an EuclideanRing model for `bool` is
|
||||
//! the same as for not providing Monoid, Group and Ring models.
|
||||
//!
|
||||
//!
|
||||
//! [1]: https://en.wikipedia.org/wiki/Euclidean_domain
|
||||
template <typename R>
|
||||
struct EuclideanRing;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_EUCLIDEAN_RING_HPP
|
||||
@@ -0,0 +1,141 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Foldable`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_FOLDABLE_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_FOLDABLE_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Foldable Foldable
|
||||
//! The `Foldable` concept represents data structures that can be reduced
|
||||
//! to a single value.
|
||||
//!
|
||||
//! Generally speaking, folding refers to the concept of summarizing a
|
||||
//! complex structure as a single value, by successively applying a
|
||||
//! binary operation which reduces two elements of the structure to a
|
||||
//! single value. Folds come in many flavors; left folds, right folds,
|
||||
//! folds with and without an initial reduction state, and their monadic
|
||||
//! variants. This concept is able to express all of these fold variants.
|
||||
//!
|
||||
//! Another way of seeing `Foldable` is as data structures supporting
|
||||
//! internal iteration with the ability to accumulate a result. By
|
||||
//! internal iteration, we mean that the _loop control_ is in the hand
|
||||
//! of the structure, not the caller. Hence, it is the structure who
|
||||
//! decides when the iteration stops, which is normally when the whole
|
||||
//! structure has been consumed. Since C++ is an eager language, this
|
||||
//! requires `Foldable` structures to be finite, or otherwise one would
|
||||
//! need to loop indefinitely to consume the whole structure.
|
||||
//!
|
||||
//! @note
|
||||
//! While the fact that `Foldable` only works for finite structures may
|
||||
//! seem overly restrictive in comparison to the Haskell definition of
|
||||
//! `Foldable`, a finer grained separation of the concepts should
|
||||
//! mitigate the issue. For iterating over possibly infinite data
|
||||
//! structures, see the `Iterable` concept. For searching a possibly
|
||||
//! infinite data structure, see the `Searchable` concept.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `fold_left` or `unpack`
|
||||
//!
|
||||
//! However, please note that a minimal complete definition provided
|
||||
//! through `unpack` will be much more compile-time efficient than one
|
||||
//! provided through `fold_left`.
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::map`, `hana::optional`, `hana::pair`, `hana::set`,
|
||||
//! `hana::range`, `hana::tuple`
|
||||
//!
|
||||
//!
|
||||
//! @anchor Foldable-lin
|
||||
//! The linearization of a `Foldable`
|
||||
//! ---------------------------------
|
||||
//! Intuitively, for a `Foldable` structure `xs`, the _linearization_ of
|
||||
//! `xs` is the sequence of all the elements in `xs` as if they had been
|
||||
//! put in a list:
|
||||
//! @code
|
||||
//! linearization(xs) = [x1, x2, ..., xn]
|
||||
//! @endcode
|
||||
//!
|
||||
//! Note that it is always possible to produce such a linearization
|
||||
//! for a finite `Foldable` by setting
|
||||
//! @code
|
||||
//! linearization(xs) = fold_left(xs, [], flip(prepend))
|
||||
//! @endcode
|
||||
//! for an appropriate definition of `[]` and `prepend`. The notion of
|
||||
//! linearization is useful for expressing various properties of
|
||||
//! `Foldable` structures, and is used across the documentation. Also
|
||||
//! note that `Iterable`s define an [extended version](@ref Iterable-lin)
|
||||
//! of this allowing for infinite structures.
|
||||
//!
|
||||
//!
|
||||
//! Compile-time Foldables
|
||||
//! ----------------------
|
||||
//! A compile-time `Foldable` is a `Foldable` whose total length is known
|
||||
//! at compile-time. In other words, it is a `Foldable` whose `length`
|
||||
//! method returns a `Constant` of an unsigned integral type. When
|
||||
//! folding a compile-time `Foldable`, the folding can be unrolled,
|
||||
//! because the final number of steps of the algorithm is known at
|
||||
//! compile-time.
|
||||
//!
|
||||
//! Additionally, the `unpack` method is only available to compile-time
|
||||
//! `Foldable`s. This is because the return _type_ of `unpack` depends
|
||||
//! on the number of objects in the structure. Being able to resolve
|
||||
//! `unpack`'s return type at compile-time hence requires the length of
|
||||
//! the structure to be known at compile-time too.
|
||||
//!
|
||||
//! __In the current version of the library, only compile-time `Foldable`s
|
||||
//! are supported.__ While it would be possible in theory to support
|
||||
//! runtime `Foldable`s too, doing so efficiently requires more research.
|
||||
//!
|
||||
//!
|
||||
//! Provided conversion to `Sequence`s
|
||||
//! ----------------------------------
|
||||
//! Given a tag `S` which is a `Sequence`, an object whose tag is a model
|
||||
//! of the `Foldable` concept can be converted to an object of tag `S`.
|
||||
//! In other words, a `Foldable` can be converted to a `Sequence` `S`, by
|
||||
//! simply taking the linearization of the `Foldable` and creating the
|
||||
//! sequence with that. More specifically, given a `Foldable` `xs` with a
|
||||
//! linearization of `[x1, ..., xn]` and a `Sequence` tag `S`, `to<S>(xs)`
|
||||
//! is equivalent to `make<S>(x1, ..., xn)`.
|
||||
//! @include example/foldable/to.cpp
|
||||
//!
|
||||
//!
|
||||
//! Free model for builtin arrays
|
||||
//! -----------------------------
|
||||
//! Builtin arrays whose size is known can be folded as-if they were
|
||||
//! homogeneous tuples. However, note that builtin arrays can't be
|
||||
//! made more than `Foldable` (e.g. `Iterable`) because they can't
|
||||
//! be empty and they also can't be returned from functions.
|
||||
//!
|
||||
//!
|
||||
//! @anchor monadic-folds
|
||||
//! Primer on monadic folds
|
||||
//! -----------------------
|
||||
//! A monadic fold is a fold in which subsequent calls to the binary
|
||||
//! function are chained with the monadic `chain` operator of the
|
||||
//! corresponding Monad. This allows a structure to be folded in a
|
||||
//! custom monadic context. For example, performing a monadic fold with
|
||||
//! the `hana::optional` monad would require the binary function to return
|
||||
//! the result as a `hana::optional`, and the fold would abort and return
|
||||
//! `nothing` whenever one of the accumulation step would fail (i.e.
|
||||
//! return `nothing`). If, however, all the reduction steps succeed,
|
||||
//! then `just` the result would be returned. Different monads will of
|
||||
//! course result in different effects.
|
||||
template <typename T>
|
||||
struct Foldable;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_FOLDABLE_HPP
|
||||
@@ -0,0 +1,139 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Functor`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_FUNCTOR_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_FUNCTOR_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Functor Functor
|
||||
//! The `Functor` concept represents types that can be mapped over.
|
||||
//!
|
||||
//! Intuitively, a [Functor][1] is some kind of box that can hold generic
|
||||
//! data and map a function over this data to create a new, transformed
|
||||
//! box. Because we are only interested in mapping a function over the
|
||||
//! contents of a black box, the only real requirement for being a functor
|
||||
//! is to provide a function which can do the mapping, along with a couple
|
||||
//! of guarantees that the mapping is well-behaved. Those requirements are
|
||||
//! made precise in the laws below. The pattern captured by `Functor` is
|
||||
//! very general, which makes it widely useful. A lot of objects can be
|
||||
//! made `Functor`s in one way or another, the most obvious example being
|
||||
//! sequences with the usual mapping of the function on each element.
|
||||
//! While this documentation will not go into much more details about
|
||||
//! the nature of functors, the [Typeclassopedia][2] is a nice
|
||||
//! Haskell-oriented resource for such information.
|
||||
//!
|
||||
//! Functors are parametric data types which are parameterized over the
|
||||
//! data type of the objects they contain. Like everywhere else in Hana,
|
||||
//! this parametricity is only at the documentation level and it is not
|
||||
//! enforced.
|
||||
//!
|
||||
//! In this library, the mapping function is called `transform` after the
|
||||
//! `std::transform` algorithm, but other programming languages have given
|
||||
//! it different names (usually `map`).
|
||||
//!
|
||||
//! @note
|
||||
//! The word _functor_ comes from functional programming, where the
|
||||
//! concept has been used for a while, notably in the Haskell programming
|
||||
//! language. Haskell people borrowed the term from [category theory][3],
|
||||
//! which, broadly speaking, is a field of mathematics dealing with
|
||||
//! abstract structures and transformations between those structures.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definitions
|
||||
//! ----------------------------
|
||||
//! 1. `transform`\n
|
||||
//! When `transform` is specified, `adjust_if` is defined analogously to
|
||||
//! @code
|
||||
//! adjust_if(xs, pred, f) = transform(xs, [](x){
|
||||
//! if pred(x) then f(x) else x
|
||||
//! })
|
||||
//! @endcode
|
||||
//!
|
||||
//! 2. `adjust_if`\n
|
||||
//! When `adjust_if` is specified, `transform` is defined analogously to
|
||||
//! @code
|
||||
//! transform(xs, f) = adjust_if(xs, always(true), f)
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! Let `xs` be a Functor with tag `F(A)`,
|
||||
//! \f$ f : A \to B \f$ and
|
||||
//! \f$ g : B \to C \f$.
|
||||
//! The following laws must be satisfied:
|
||||
//! @code
|
||||
//! transform(xs, id) == xs
|
||||
//! transform(xs, compose(g, f)) == transform(transform(xs, f), g)
|
||||
//! @endcode
|
||||
//! The first line says that mapping the identity function should not do
|
||||
//! anything, which precludes the functor from doing something nasty
|
||||
//! behind the scenes. The second line states that mapping the composition
|
||||
//! of two functions is the same as mapping the first function, and then
|
||||
//! the second on the result. While the usual functor laws are usually
|
||||
//! restricted to the above, this library includes other convenience
|
||||
//! methods and they should satisfy the following equations.
|
||||
//! Let `xs` be a Functor with tag `F(A)`,
|
||||
//! \f$ f : A \to A \f$,
|
||||
//! \f$ \mathrm{pred} : A \to \mathrm{Bool} \f$
|
||||
//! for some `Logical` `Bool`, and `oldval`, `newval`, `value` objects
|
||||
//! of tag `A`. Then,
|
||||
//! @code
|
||||
//! adjust(xs, value, f) == adjust_if(xs, equal.to(value), f)
|
||||
//! adjust_if(xs, pred, f) == transform(xs, [](x){
|
||||
//! if pred(x) then f(x) else x
|
||||
//! })
|
||||
//! replace_if(xs, pred, value) == adjust_if(xs, pred, always(value))
|
||||
//! replace(xs, oldval, newval) == replace_if(xs, equal.to(oldval), newval)
|
||||
//! fill(xs, value) == replace_if(xs, always(true), value)
|
||||
//! @endcode
|
||||
//! The default definition of the methods will satisfy these equations.
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::lazy`, `hana::optional`, `hana::tuple`
|
||||
//!
|
||||
//!
|
||||
//! Structure-preserving functions for Functors
|
||||
//! -------------------------------------------
|
||||
//! A mapping between two functors which also preserves the functor
|
||||
//! laws is called a natural transformation (the term comes from
|
||||
//! category theory). A natural transformation is a function `f`
|
||||
//! from a functor `F` to a functor `G` such that for every other
|
||||
//! function `g` with an appropriate signature and for every object
|
||||
//! `xs` of tag `F(X)`,
|
||||
//! @code
|
||||
//! f(transform(xs, g)) == transform(f(xs), g)
|
||||
//! @endcode
|
||||
//!
|
||||
//! There are several examples of such transformations, like `to<tuple_tag>`
|
||||
//! when applied to an optional value. Indeed, for any function `g` and
|
||||
//! `hana::optional` `opt`,
|
||||
//! @code
|
||||
//! to<tuple_tag>(transform(opt, g)) == transform(to<tuple_tag>(opt), g)
|
||||
//! @endcode
|
||||
//!
|
||||
//! Of course, natural transformations are not limited to the `to<...>`
|
||||
//! functions. However, note that any conversion function between Functors
|
||||
//! should be natural for the behavior of the conversion to be intuitive.
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Functor
|
||||
//! [2]: https://wiki.haskell.org/Typeclassopedia#Functor
|
||||
//! [3]: http://en.wikipedia.org/wiki/Category_theory
|
||||
template <typename F>
|
||||
struct Functor;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_FUNCTOR_HPP
|
||||
@@ -0,0 +1,111 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Group`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_GROUP_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_GROUP_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Group Group
|
||||
//! The `Group` concept represents `Monoid`s where all objects have
|
||||
//! an inverse w.r.t. the `Monoid`'s binary operation.
|
||||
//!
|
||||
//! A [Group][1] is an algebraic structure built on top of a `Monoid`
|
||||
//! which adds the ability to invert the action of the `Monoid`'s binary
|
||||
//! operation on any element of the set. Specifically, a `Group` is a
|
||||
//! `Monoid` `(S, +)` such that every element `s` in `S` has an inverse
|
||||
//! (say `s'`) which is such that
|
||||
//! @code
|
||||
//! s + s' == s' + s == identity of the Monoid
|
||||
//! @endcode
|
||||
//!
|
||||
//! There are many examples of `Group`s, one of which would be the
|
||||
//! additive `Monoid` on integers, where the inverse of any integer
|
||||
//! `n` is the integer `-n`. The method names used here refer to
|
||||
//! exactly this model.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definitions
|
||||
//! ----------------------------
|
||||
//! 1. `minus`\n
|
||||
//! When `minus` is specified, the `negate` method is defaulted by setting
|
||||
//! @code
|
||||
//! negate(x) = minus(zero<G>(), x)
|
||||
//! @endcode
|
||||
//!
|
||||
//! 2. `negate`\n
|
||||
//! When `negate` is specified, the `minus` method is defaulted by setting
|
||||
//! @code
|
||||
//! minus(x, y) = plus(x, negate(y))
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! For all objects `x` of a `Group` `G`, the following laws must be
|
||||
//! satisfied:
|
||||
//! @code
|
||||
//! plus(x, negate(x)) == zero<G>() // right inverse
|
||||
//! plus(negate(x), x) == zero<G>() // left inverse
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Refined concept
|
||||
//! ---------------
|
||||
//! `Monoid`
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`
|
||||
//!
|
||||
//!
|
||||
//! Free model for non-boolean arithmetic data types
|
||||
//! ------------------------------------------------
|
||||
//! A data type `T` is arithmetic if `std::is_arithmetic<T>::%value` is
|
||||
//! true. For a non-boolean arithmetic data type `T`, a model of `Group`
|
||||
//! is automatically defined by setting
|
||||
//! @code
|
||||
//! minus(x, y) = (x - y)
|
||||
//! negate(x) = -x
|
||||
//! @endcode
|
||||
//!
|
||||
//! @note
|
||||
//! The rationale for not providing a Group model for `bool` is the same
|
||||
//! as for not providing a `Monoid` model.
|
||||
//!
|
||||
//!
|
||||
//! Structure-preserving functions
|
||||
//! ------------------------------
|
||||
//! Let `A` and `B` be two `Group`s. A function `f : A -> B` is said to
|
||||
//! be a [Group morphism][2] if it preserves the group structure between
|
||||
//! `A` and `B`. Rigorously, for all objects `x, y` of data type `A`,
|
||||
//! @code
|
||||
//! f(plus(x, y)) == plus(f(x), f(y))
|
||||
//! @endcode
|
||||
//! Because of the `Group` structure, it is easy to prove that the
|
||||
//! following will then also be satisfied:
|
||||
//! @code
|
||||
//! f(negate(x)) == negate(f(x))
|
||||
//! f(zero<A>()) == zero<B>()
|
||||
//! @endcode
|
||||
//! Functions with these properties interact nicely with `Group`s, which
|
||||
//! is why they are given such a special treatment.
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Group_(mathematics)
|
||||
//! [2]: http://en.wikipedia.org/wiki/Group_homomorphism
|
||||
template <typename G>
|
||||
struct Group;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_GROUP_HPP
|
||||
@@ -0,0 +1,68 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Hashable`.
|
||||
|
||||
@copyright Louis Dionne 2016
|
||||
@copyright Jason Rice 2016
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_HASHABLE_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_HASHABLE_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Hashable Hashable
|
||||
//! The `Hashable` concept represents objects that can be normalized to
|
||||
//! a type-level hash.
|
||||
//!
|
||||
//! In day to day programming, hashes are very important as a way to
|
||||
//! efficiently lookup objects in maps. While the implementation of
|
||||
//! maps is very different, the same idea of using hashes for efficient
|
||||
//! lookup applies in metaprogramming. The `Hashable` concept represents
|
||||
//! objects that can be summarized (possibly with loss of information) to
|
||||
//! a type, in a way suitable for use in hash-based data structures. Of
|
||||
//! course, in order for a hash to be well-behaved, it must obey some laws
|
||||
//! that are explained below.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `hash`, satisfying the laws below
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! First, `hana::hash` must return a `hana::type`. Furthermore, for any
|
||||
//! two `Hashable` objects `x` and `y`, it must be the case that
|
||||
//! @code
|
||||
//! x == y implies hash(x) == hash(y)
|
||||
//! @endcode
|
||||
//!
|
||||
//! where `==` denotes `hana::equal`. In other words, any two objects that
|
||||
//! compare equal (with `hana::equal`) must also have the same hash.
|
||||
//! However, the reverse is not true, and two different objects may have
|
||||
//! the same hash. This situation of two different objects having the same
|
||||
//! hash is called a _collision_.
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`, `hana::type`, `hana::string`
|
||||
//!
|
||||
//!
|
||||
//! Free model for `IntegralConstant`s
|
||||
//! ----------------------------------
|
||||
//! Any `IntegralConstant` is `Hashable`, by normalizing its value to a
|
||||
//! `hana::integral_constant`. The type of the value held in the normalized
|
||||
//! `integral_constant` is `unsigned long long` for unsigned integral
|
||||
//! types, and `signed long long` for signed integral types.
|
||||
template <typename T>
|
||||
struct Hashable;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_HASHABLE_HPP
|
||||
@@ -0,0 +1,73 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::IntegralConstant`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_INTEGRAL_CONSTANT_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_INTEGRAL_CONSTANT_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! The `IntegralConstant` concept represents compile-time integral values.
|
||||
//!
|
||||
//! The `IntegralConstant` concept represents objects that hold a
|
||||
//! `constexpr` value of an integral type. In other words, it describes
|
||||
//! the essential functionality provided by `std::integral_constant`.
|
||||
//! An `IntegralConstant` is also just a special kind of `Constant`
|
||||
//! whose inner value is of an integral type.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! The requirements for being an `IntegralConstant` are quite simple.
|
||||
//! First, an `IntegralConstant` `C` must be a `Constant` such that
|
||||
//! `Tag::value_type` is an integral type, where `Tag` is the tag of `C`.
|
||||
//!
|
||||
//! Secondly, `C` must have a nested `static constexpr` member named
|
||||
//! `value`, such that the following code is valid:
|
||||
//! @code
|
||||
//! constexpr auto v = C::value;
|
||||
//! @endcode
|
||||
//! Because of the requirement that `Tag::value_type` be an integral type,
|
||||
//! it follows that `C::value` must be an integral value.
|
||||
//!
|
||||
//! Finally, it is necessary to specialize the `IntegralConstant` template
|
||||
//! in the `boost::hana` namespace to tell Hana that a type is a model
|
||||
//! of `IntegralConstant`:
|
||||
//! @code
|
||||
//! namespace boost { namespace hana {
|
||||
//! template <>
|
||||
//! struct IntegralConstant<your_custom_tag> {
|
||||
//! static constexpr bool value = true;
|
||||
//! };
|
||||
//! }}
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Refined concept
|
||||
//! ---------------
|
||||
//! 1. `Constant` (free implementation of `value`)\n
|
||||
//! The `value` function required to be a `Constant` can be implemented
|
||||
//! as follows for `IntegralConstant`s:
|
||||
//! @code
|
||||
//! value<C>() == C::value
|
||||
//! @endcode
|
||||
//! The `to` function must still be provided explicitly for the model
|
||||
//! of `Constant` to be complete.
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`
|
||||
template <typename C>
|
||||
struct IntegralConstant;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_INTEGRAL_CONSTANT_HPP
|
||||
@@ -0,0 +1,149 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Iterable`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_ITERABLE_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_ITERABLE_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Iterable Iterable
|
||||
//! The `Iterable` concept represents data structures supporting external
|
||||
//! iteration.
|
||||
//!
|
||||
//! Intuitively, an `Iterable` can be seen as a kind of container whose
|
||||
//! elements can be pulled out one at a time. An `Iterable` also provides
|
||||
//! a way to know when the _container_ is empty, i.e. when there are no
|
||||
//! more elements to pull out.
|
||||
//!
|
||||
//! Whereas `Foldable` represents data structures supporting internal
|
||||
//! iteration with the ability to accumulate a result, the `Iterable`
|
||||
//! concept allows inverting the control of the iteration. This is more
|
||||
//! flexible than `Foldable`, since it allows iterating over only some
|
||||
//! part of the structure. This, in turn, allows `Iterable` to work on
|
||||
//! infinite structures, while trying to fold such a structure would
|
||||
//! never finish.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `at`, `drop_front` and `is_empty`
|
||||
//!
|
||||
//!
|
||||
//! @anchor Iterable-lin
|
||||
//! The linearization of an `Iterable`
|
||||
//! ----------------------------------
|
||||
//! Intuitively, for an `Iterable` structure `xs`, the _linearization_ of
|
||||
//! `xs` is the sequence of all the elements in `xs` as if they had been
|
||||
//! put in a (possibly infinite) list:
|
||||
//! @code
|
||||
//! linearization(xs) = [x1, x2, x3, ...]
|
||||
//! @endcode
|
||||
//!
|
||||
//! The `n`th element of the linearization of an `Iterable` can be
|
||||
//! accessed with the `at` function. In other words, `at(xs, n) == xn`.
|
||||
//!
|
||||
//! Note that this notion is precisely the extension of the [linearization]
|
||||
//! (@ref Foldable-lin) notion of `Foldable`s to the infinite case. This
|
||||
//! notion is useful for expressing various properties of `Iterable`s,
|
||||
//! and is used for that elsewhere in the documentation.
|
||||
//!
|
||||
//!
|
||||
//! Compile-time `Iterable`s
|
||||
//! ------------------------
|
||||
//! A _compile-time_ `Iterable` is an `Iterable` for which `is_empty`
|
||||
//! returns a compile-time `Logical`. These structures allow iteration
|
||||
//! to be done at compile-time, in the sense that the "loop" doing the
|
||||
//! iteration can be unrolled because the total length of the structure
|
||||
//! is kown at compile-time.
|
||||
//!
|
||||
//! In particular, note that being a compile-time `Iterable` has nothing
|
||||
//! to do with being finite or infinite. For example, it would be possible
|
||||
//! to create a sequence representing the Pythagorean triples as
|
||||
//! `integral_constant`s. Such a sequence would be infinite, but iteration
|
||||
//! on the sequence would still be done at compile-time. However, if one
|
||||
//! tried to iterate over _all_ the elements of the sequence, the compiler
|
||||
//! would loop indefinitely, in contrast to your program looping
|
||||
//! indefinitely if the sequence was a runtime one.
|
||||
//!
|
||||
//! __In the current version of the library, only compile-time `Iterable`s
|
||||
//! are supported.__ While it would be possible in theory to support
|
||||
//! runtime `Iterable`s, doing it efficiently is the subject of some
|
||||
//! research. In particular, follow [this issue][1] for the current
|
||||
//! status of runtime `Iterable`s.
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! First, we require the equality of two `Iterable`s to be related to the
|
||||
//! equality of the elements in their linearizations. More specifically,
|
||||
//! if `xs` and `ys` are two `Iterable`s of data type `It`, then
|
||||
//! @code
|
||||
//! xs == ys => at(xs, i) == at(ys, i) for all i
|
||||
//! @endcode
|
||||
//!
|
||||
//! This conveys that two `Iterable`s must have the same linearization
|
||||
//! in order to be considered equal.
|
||||
//!
|
||||
//! Secondly, since every `Iterable` is also a `Searchable`, we require
|
||||
//! the models of `Iterable` and `Searchable` to be consistent. This is
|
||||
//! made precise by the following laws. For any `Iterable` `xs` with a
|
||||
//! linearization of `[x1, x2, x3, ...]`,
|
||||
//! @code
|
||||
//! any_of(xs, equal.to(z)) <=> xi == z
|
||||
//! @endcode
|
||||
//! for some _finite_ index `i`. Furthermore,
|
||||
//! @code
|
||||
//! find_if(xs, pred) == just(the first xi such that pred(xi) is satisfied)
|
||||
//! @endcode
|
||||
//! or `nothing` if no such `xi` exists.
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! 1. `Searchable` (free model)\n
|
||||
//! Any `Iterable` gives rise to a model of `Searchable`, where the keys
|
||||
//! and the values are both the elements in the structure. Searching for
|
||||
//! a key is just doing a linear search through the elements of the
|
||||
//! structure.
|
||||
//! @include example/iterable/searchable.cpp
|
||||
//!
|
||||
//! 2. `Foldable` for finite `Iterable`s\n
|
||||
//! Every finite `Iterable` gives rise to a model of `Foldable`. For
|
||||
//! these models to be consistent, we require the models of both `Foldable`
|
||||
//! and `Iterable` to have the same linearization.
|
||||
//!
|
||||
//! @note
|
||||
//! As explained above, `Iterable`s are also `Searchable`s and their
|
||||
//! models have to be consistent. By the laws presented here, it also
|
||||
//! means that the `Foldable` model for finite `Iterable`s has to be
|
||||
//! consistent with the `Searchable` model.
|
||||
//!
|
||||
//! For convenience, finite `Iterable`s must only provide a definition of
|
||||
//! `length` to model the `Foldable` concept; defining the more powerful
|
||||
//! `unpack` or `fold_left` is not necessary (but still possible). The
|
||||
//! default implementation of `unpack` derived from `Iterable` + `length`
|
||||
//! uses the fact that `at(xs, i)` denotes the `i`th element of `xs`'s
|
||||
//! linearization, and that the linearization of a finite `Iterable` must
|
||||
//! be the same as its linearization as a `Foldable`.
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::tuple`, `hana::string`, `hana::range`
|
||||
//!
|
||||
//!
|
||||
//! [1]: https://github.com/boostorg/hana/issues/40
|
||||
template <typename It>
|
||||
struct Iterable;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_ITERABLE_HPP
|
||||
@@ -0,0 +1,166 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Logical`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_LOGICAL_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_LOGICAL_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Logical Logical
|
||||
//! The `Logical` concept represents types with a truth value.
|
||||
//!
|
||||
//! Intuitively, a `Logical` is just a `bool`, or something that can act
|
||||
//! like one. However, in the context of programming with heterogeneous
|
||||
//! objects, it becomes extremely important to distinguish between those
|
||||
//! objects whose truth value is known at compile-time, and those whose
|
||||
//! truth value is only known at runtime. The reason why this is so
|
||||
//! important is because it is possible to branch at compile-time on
|
||||
//! a condition whose truth value is known at compile-time, and hence
|
||||
//! the return type of the enclosing function can depend on that truth
|
||||
//! value. However, if the truth value is only known at runtime, then
|
||||
//! the compiler has to compile both branches (because any or both of
|
||||
//! them may end up being used), which creates the additional requirement
|
||||
//! that both branches must evaluate to the same type.
|
||||
//!
|
||||
//! More specifically, `Logical` (almost) represents a [boolean algebra][1],
|
||||
//! which is a mathematical structure encoding the usual properties that
|
||||
//! allow us to reason with `bool`. The exact properties that must be
|
||||
//! satisfied by any model of `Logical` are rigorously stated in the laws
|
||||
//! below.
|
||||
//!
|
||||
//!
|
||||
//! Truth, falsity and logical equivalence
|
||||
//! --------------------------------------
|
||||
//! A `Logical` `x` is said to be _true-valued_, or sometimes also just
|
||||
//! _true_ as an abuse of notation, if
|
||||
//! @code
|
||||
//! if_(x, true, false) == true
|
||||
//! @endcode
|
||||
//!
|
||||
//! Similarly, `x` is _false-valued_, or sometimes just _false_, if
|
||||
//! @code
|
||||
//! if_(x, true, false) == false
|
||||
//! @endcode
|
||||
//!
|
||||
//! This provides a standard way of converting any `Logical` to a straight
|
||||
//! `bool`. The notion of truth value suggests another definition, which
|
||||
//! is that of logical equivalence. We will say that two `Logical`s `x`
|
||||
//! and `y` are _logically equivalent_ if they have the same truth value.
|
||||
//! To denote that some expressions `p` and `q` of a Logical data type are
|
||||
//! logically equivalent, we will sometimes also write
|
||||
//! @code
|
||||
//! p if and only if q
|
||||
//! @endcode
|
||||
//! which is very common in mathematics. The intuition behind this notation
|
||||
//! is that whenever `p` is true-valued, then `q` should be; but when `p`
|
||||
//! is false-valued, then `q` should be too. Hence, `p` should be
|
||||
//! true-valued when (and only when) `q` is true-valued.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `eval_if`, `not_` and `while_`
|
||||
//!
|
||||
//! All the other functions can be defined in those terms:
|
||||
//! @code
|
||||
//! if_(cond, x, y) = eval_if(cond, lazy(x), lazy(y))
|
||||
//! and_(x, y) = if_(x, y, x)
|
||||
//! or_(x, y) = if_(x, x, y)
|
||||
//! etc...
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! As outlined above, the `Logical` concept almost represents a boolean
|
||||
//! algebra. The rationale for this laxity is to allow things like integers
|
||||
//! to act like `Logical`s, which is aligned with C++, even though they do
|
||||
//! not form a boolean algebra. Even though we depart from the usual
|
||||
//! axiomatization of boolean algebras, we have found through experience
|
||||
//! that the definition of a Logical given here is largely compatible with
|
||||
//! intuition.
|
||||
//!
|
||||
//! The following laws must be satisfied for any data type `L` modeling
|
||||
//! the `Logical` concept. Let `a`, `b` and `c` be objects of a `Logical`
|
||||
//! data type, and let `t` and `f` be arbitrary _true-valued_ and
|
||||
//! _false-valued_ `Logical`s of that data type, respectively. Then,
|
||||
//! @code
|
||||
//! // associativity
|
||||
//! or_(a, or_(b, c)) == or_(or_(a, b), c)
|
||||
//! and_(a, and_(b, c)) == and_(and_(a, b), c)
|
||||
//!
|
||||
//! // equivalence through commutativity
|
||||
//! or_(a, b) if and only if or_(b, a)
|
||||
//! and_(a, b) if and only if and_(b, a)
|
||||
//!
|
||||
//! // absorption
|
||||
//! or_(a, and_(a, b)) == a
|
||||
//! and_(a, or_(a, b)) == a
|
||||
//!
|
||||
//! // left identity
|
||||
//! or_(a, f) == a
|
||||
//! and_(a, t) == a
|
||||
//!
|
||||
//! // distributivity
|
||||
//! or_(a, and_(b, c)) == and_(or_(a, b), or_(a, c))
|
||||
//! and_(a, or_(b, c)) == or_(and_(a, b), and_(a, c))
|
||||
//!
|
||||
//! // complements
|
||||
//! or_(a, not_(a)) is true-valued
|
||||
//! and_(a, not_(a)) is false-valued
|
||||
//! @endcode
|
||||
//!
|
||||
//! > #### Why is the above not a boolean algebra?
|
||||
//! > If you look closely, you will find that we depart from the usual
|
||||
//! > boolean algebras because:
|
||||
//! > 1. we do not require the elements representing truth and falsity to
|
||||
//! > be unique
|
||||
//! > 2. we do not enforce commutativity of the `and_` and `or_` operations
|
||||
//! > 3. because we do not enforce commutativity, the identity laws become
|
||||
//! > left-identity laws
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`
|
||||
//!
|
||||
//!
|
||||
//! Free model for arithmetic data types
|
||||
//! ------------------------------------
|
||||
//! A data type `T` is arithmetic if `std::is_arithmetic<T>::%value` is
|
||||
//! true. For an arithmetic data type `T`, a model of `Logical` is
|
||||
//! provided automatically by using the result of the builtin implicit
|
||||
//! conversion to `bool` as a truth value. Specifically, the minimal
|
||||
//! complete definition for those data types is
|
||||
//! @code
|
||||
//! eval_if(cond, then, else_) = cond ? then(id) : else(id)
|
||||
//! not_(cond) = static_cast<T>(cond ? false : true)
|
||||
//! while_(pred, state, f) = equivalent to a normal while loop
|
||||
//! @endcode
|
||||
//!
|
||||
//! > #### Rationale for not providing a model for all contextually convertible to bool data types
|
||||
//! > The `not_` method can not be implemented in a meaningful way for all
|
||||
//! > of those types. For example, one can not cast a pointer type `T*`
|
||||
//! > to bool and then back again to `T*` in a meaningful way. With an
|
||||
//! > arithmetic type `T`, however, it is possible to cast from `T` to
|
||||
//! > bool and then to `T` again; the result will be `0` or `1` depending
|
||||
//! > on the truth value. If you want to use a pointer type or something
|
||||
//! > similar in a conditional, it is suggested to explicitly convert it
|
||||
//! > to bool by using `to<bool>`.
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Boolean_algebra_(structure)
|
||||
template <typename L>
|
||||
struct Logical;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_LOGICAL_HPP
|
||||
@@ -0,0 +1,99 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Metafunction`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_METAFUNCTION_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_METAFUNCTION_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Metafunction Metafunction
|
||||
//! A `Metafunction` is a function that takes `hana::type`s as inputs and
|
||||
//! returns a `hana::type` as output.
|
||||
//!
|
||||
//! A `Metafunction` is an object satisfying the [FunctionObject][1]
|
||||
//! concept, but with additional requirements. First, it must be possible
|
||||
//! to apply a `Metafunction` to arguments whose tag is `type_tag`, and
|
||||
//! the result of such an application must be an object whose tag is also
|
||||
//! `type_tag`. Note that `hana::type` and `hana::basic_type` are the
|
||||
//! only such types.
|
||||
//!
|
||||
//! Secondly, a `Metafunction` must provide a nested `::%apply` template
|
||||
//! which allows performing the same type-level computation as is done by
|
||||
//! the call operator. In Boost.MPL parlance, a `Metafunction` `F` is
|
||||
//! hence a [MetafunctionClass][2] in addition to being a `FunctionObject`.
|
||||
//! Rigorously, the following must be satisfied by any object `f` of type
|
||||
//! `F` which is a `Metafunction`, and for arbitrary types `T...`:
|
||||
//! @code
|
||||
//! f(hana::type_c<T>...) == hana::type_c<F::apply<T...>::type>
|
||||
//! @endcode
|
||||
//!
|
||||
//! Thirdly, to ease the inter-operation of values and types,
|
||||
//! `Metafunction`s must also allow being called with arguments that
|
||||
//! are not `hana::type`s. In that case, the result is equivalent to
|
||||
//! calling the metafunction on the types of the arguments. Rigorously,
|
||||
//! this means that for arbitrary objects `x...`,
|
||||
//! @code
|
||||
//! f(x...) == f(hana::type_c<decltype(x)>...)
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! The `Metafunction` concept does not have a minimal complete definition
|
||||
//! in terms of tag-dispatched methods. Instead, the syntactic requirements
|
||||
//! documented above should be satisfied, and the `Metafunction` struct
|
||||
//! should be specialized explicitly in Hana's namespace.
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::metafunction`, `hana::metafunction_class`, `hana::template_`
|
||||
//!
|
||||
//!
|
||||
//! Rationale: Why aren't `Metafunction`s `Comparable`?
|
||||
//! ---------------------------------------------------
|
||||
//! When seeing `hana::template_`, a question that naturally arises is
|
||||
//! whether `Metafunction`s should be made `Comparable`. Indeed, it
|
||||
//! would seem to make sense to compare two templates `F` and `G` with
|
||||
//! `template_<F> == template_<G>`. However, in the case where `F` and/or
|
||||
//! `G` are alias templates, it makes sense to talk about two types of
|
||||
//! comparisons. The first one is _shallow_ comparison, and it determines
|
||||
//! that two alias templates are equal if they are the same alias
|
||||
//! template. The second one is _deep_ comparison, and it determines
|
||||
//! that two template aliases are equal if they alias the same type for
|
||||
//! any template argument. For example, given `F` and `G` defined as
|
||||
//! @code
|
||||
//! template <typename T>
|
||||
//! using F = void;
|
||||
//!
|
||||
//! template <typename T>
|
||||
//! using G = void;
|
||||
//! @endcode
|
||||
//!
|
||||
//! shallow comparison would determine that `F` and `G` are different
|
||||
//! because they are two different template aliases, while deep comparison
|
||||
//! would determine that `F` and `G` are equal because they always
|
||||
//! expand to the same type, `void`. Unfortunately, deep comparison is
|
||||
//! impossible to implement because one would have to check `F` and `G`
|
||||
//! on all possible types. On the other hand, shallow comparison is not
|
||||
//! satisfactory because `Metafunction`s are nothing but functions on
|
||||
//! `type`s, and the equality of two functions is normally defined with
|
||||
//! deep comparison. Hence, we adopt a conservative stance and avoid
|
||||
//! providing comparison for `Metafunction`s.
|
||||
//!
|
||||
//! [1]: http://en.cppreference.com/w/cpp/concept/FunctionObject
|
||||
//! [2]: http://www.boost.org/doc/libs/release/libs/mpl/doc/refmanual/metafunction-class.html
|
||||
template <typename F>
|
||||
struct Metafunction;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_METAFUNCTION_HPP
|
||||
@@ -0,0 +1,198 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Monad`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_MONAD_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_MONAD_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Monad Monad
|
||||
//! The `Monad` concept represents `Applicative`s with the ability to
|
||||
//! flatten nested levels of structure.
|
||||
//!
|
||||
//! Historically, Monads are a construction coming from category theory,
|
||||
//! an abstract branch of mathematics. The functional programming
|
||||
//! community eventually discovered how Monads could be used to
|
||||
//! formalize several useful things like side effects, which led
|
||||
//! to the wide adoption of Monads in that community. However, even
|
||||
//! in a multi-paradigm language like C++, there are several constructs
|
||||
//! which turn out to be Monads, like `std::optional`, `std::vector` and
|
||||
//! others.
|
||||
//!
|
||||
//! Everybody tries to introduce `Monad`s with a different analogy, and
|
||||
//! most people fail. This is called the [Monad tutorial fallacy][1]. We
|
||||
//! will try to avoid this trap by not presenting a specific intuition,
|
||||
//! and we will instead present what monads are mathematically.
|
||||
//! For specific intuitions, we will let readers who are new to this
|
||||
//! concept read one of the many excellent tutorials available online.
|
||||
//! Understanding Monads might take time at first, but once you get it,
|
||||
//! a lot of patterns will become obvious Monads; this enlightening will
|
||||
//! be your reward for the hard work.
|
||||
//!
|
||||
//! There are different ways of defining a Monad; Haskell uses a function
|
||||
//! called `bind` (`>>=`) and another one called `return` (it has nothing
|
||||
//! to do with C++'s `return` statement). They then introduce relationships
|
||||
//! that must be satisfied for a type to be a Monad with those functions.
|
||||
//! Mathematicians sometimes use a function called `join` and another one
|
||||
//! called `unit`, or they also sometimes use other category theoretic
|
||||
//! constructions like functor adjunctions and the Kleisli category.
|
||||
//!
|
||||
//! This library uses a composite approach. First, we use the `flatten`
|
||||
//! function (equivalent to `join`) along with the `lift` function from
|
||||
//! `Applicative` (equivalent to `unit`) to introduce the notion of
|
||||
//! monadic function composition. We then write the properties that must
|
||||
//! be satisfied by a Monad using this monadic composition operator,
|
||||
//! because we feel it shows the link between Monads and Monoids more
|
||||
//! clearly than other approaches.
|
||||
//!
|
||||
//! Roughly speaking, we will say that a `Monad` is an `Applicative` which
|
||||
//! also defines a way to compose functions returning a monadic result,
|
||||
//! as opposed to only being able to compose functions returning a normal
|
||||
//! result. We will then ask for this composition to be associative and to
|
||||
//! have a neutral element, just like normal function composition. For
|
||||
//! usual composition, the neutral element is the identity function `id`.
|
||||
//! For monadic composition, the neutral element is the `lift` function
|
||||
//! defined by `Applicative`. This construction is made clearer in the
|
||||
//! laws below.
|
||||
//!
|
||||
//! @note
|
||||
//! Monads are known to be a big chunk to swallow. However, it is out of
|
||||
//! the scope of this documentation to provide a full-blown explanation
|
||||
//! of the concept. The [Typeclassopedia][2] is a nice Haskell-oriented
|
||||
//! resource where more information about Monads can be found.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definitions
|
||||
//! ----------------------------
|
||||
//! First, a `Monad` must be both a `Functor` and an `Applicative`.
|
||||
//! Also, an implementation of `flatten` or `chain` satisfying the
|
||||
//! laws below for monadic composition must be provided.
|
||||
//!
|
||||
//! @note
|
||||
//! The `ap` method for `Applicatives` may be derived from the minimal
|
||||
//! complete definition of `Monad` and `Functor`; see below for more
|
||||
//! information.
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! To simplify writing the laws, we use the comparison between functions.
|
||||
//! For two functions `f` and `g`, we define
|
||||
//! @code
|
||||
//! f == g if and only if f(x) == g(x) for all x
|
||||
//! @endcode
|
||||
//!
|
||||
//! With the usual composition of functions, we are given two functions
|
||||
//! @f$ f : A \to B @f$ and @f$ g : B \to C @f$, and we must produce a
|
||||
//! new function @f$ compose(g, f) : A \to C @f$. This composition of
|
||||
//! functions is associative, which means that
|
||||
//! @code
|
||||
//! compose(h, compose(g, f)) == compose(compose(h, g), f)
|
||||
//! @endcode
|
||||
//!
|
||||
//! Also, this composition has an identity element, which is the identity
|
||||
//! function. This simply means that
|
||||
//! @code
|
||||
//! compose(f, id) == compose(id, f) == f
|
||||
//! @endcode
|
||||
//!
|
||||
//! This is probably nothing new if you are reading the `Monad` laws.
|
||||
//! Now, we can observe that the above is equivalent to saying that
|
||||
//! functions with the composition operator form a `Monoid`, where the
|
||||
//! neutral element is the identity function.
|
||||
//!
|
||||
//! Given an `Applicative` `F`, what if we wanted to compose two functions
|
||||
//! @f$ f : A \to F(B) @f$ and @f$ g : B \to F(C) @f$? When the
|
||||
//! `Applicative` `F` is also a `Monad`, such functions taking normal
|
||||
//! values but returning monadic values are called _monadic functions_.
|
||||
//! To compose them, we obviously can't use normal function composition,
|
||||
//! since the domains and codomains of `f` and `g` do not match properly.
|
||||
//! Instead, we'll need a new operator -- let's call it `monadic_compose`:
|
||||
//! @f[
|
||||
//! \mathtt{monadic\_compose} :
|
||||
//! (B \to F(C)) \times (A \to F(B)) \to (A \to F(C))
|
||||
//! @f]
|
||||
//!
|
||||
//! How could we go about implementing this function? Well, since we know
|
||||
//! `F` is an `Applicative`, the only functions we have are `transform`
|
||||
//! (from `Functor`), and `lift` and `ap` (from `Applicative`). Hence,
|
||||
//! the only thing we can do at this point while respecting the signatures
|
||||
//! of `f` and `g` is to set (for `x` of type `A`)
|
||||
//! @code
|
||||
//! monadic_compose(g, f)(x) = transform(f(x), g)
|
||||
//! @endcode
|
||||
//!
|
||||
//! Indeed, `f(x)` is of type `F(B)`, so we can map `g` (which takes `B`'s)
|
||||
//! on it. Doing so will leave us with a result of type `F(F(C))`, but what
|
||||
//! we wanted was a result of type `F(C)` to respect the signature of
|
||||
//! `monadic_compose`. If we had a joker of type @f$ F(F(C)) \to F(C) @f$,
|
||||
//! we could simply set
|
||||
//! @code
|
||||
//! monadic_compose(g, f)(x) = joker(transform(f(x), g))
|
||||
//! @endcode
|
||||
//!
|
||||
//! and we would be happy. It turns out that `flatten` is precisely this
|
||||
//! joker. Now, we'll want our joker to satisfy some properties to make
|
||||
//! sure this composition is associative, just like our normal composition
|
||||
//! was. These properties are slightly cumbersome to specify, so we won't
|
||||
//! do it here. Also, we'll need some kind of neutral element for the
|
||||
//! composition. This neutral element can't be the usual identity function,
|
||||
//! because it does not have the right type: our neutral element needs to
|
||||
//! be a function of type @f$ X \to F(X) @f$ but the identity function has
|
||||
//! type @f$ X \to X @f$. It is now the right time to observe that `lift`
|
||||
//! from `Applicative` has exactly the right signature, and so we'll take
|
||||
//! this for our neutral element.
|
||||
//!
|
||||
//! We are now ready to formulate the `Monad` laws using this composition
|
||||
//! operator. For a `Monad` `M` and functions @f$ f : A \to M(B) @f$,
|
||||
//! @f$ g : B \to M(C) @f$ and @f$ h : C \to M(D) @f$, the following
|
||||
//! must be satisfied:
|
||||
//! @code
|
||||
//! // associativity
|
||||
//! monadic_compose(h, monadic_compose(g, f)) == monadic_compose(monadic_compose(h, g), f)
|
||||
//!
|
||||
//! // right identity
|
||||
//! monadic_compose(f, lift<M(A)>) == f
|
||||
//!
|
||||
//! // left identity
|
||||
//! monadic_compose(lift<M(B)>, f) == f
|
||||
//! @endcode
|
||||
//!
|
||||
//! which is to say that `M` along with monadic composition is a Monoid
|
||||
//! where the neutral element is `lift`.
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! 1. `Functor`
|
||||
//! 2. `Applicative` (free implementation of `ap`)\n
|
||||
//! When the minimal complete definition for `Monad` and `Functor` are
|
||||
//! both satisfied, it is possible to implement `ap` by setting
|
||||
//! @code
|
||||
//! ap(fs, xs) = chain(fs, [](auto f) {
|
||||
//! return transform(xs, f);
|
||||
//! })
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::lazy`, `hana::optional`, `hana::tuple`
|
||||
//!
|
||||
//!
|
||||
//! [1]: https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/
|
||||
//! [2]: https://wiki.haskell.org/Typeclassopedia#Monad
|
||||
template <typename M>
|
||||
struct Monad;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_MONAD_HPP
|
||||
@@ -0,0 +1,89 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::MonadPlus`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_MONAD_PLUS_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_MONAD_PLUS_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-MonadPlus MonadPlus
|
||||
//! The `MonadPlus` concept represents Monads with a monoidal structure.
|
||||
//!
|
||||
//! Intuitively, whereas a Monad can be seen as some kind of container
|
||||
//! or context, a MonadPlus can be seen as a container or a context that
|
||||
//! can be concatenated with other containers or contexts. There must
|
||||
//! also be an identity element for this combining operation. For example,
|
||||
//! a tuple is a MonadPlus, because tuples can be concatenated and the
|
||||
//! empty tuple would act as an identity for concatenation. How is this
|
||||
//! different from a Monad which is also a Monoid? The answer is that the
|
||||
//! monoidal structure on a MonadPlus must _not_ depend of the contents
|
||||
//! of the structure; it must not require the contents to be a Monoid
|
||||
//! in order to work.
|
||||
//!
|
||||
//! While sequences are not the only possible model for MonadPlus, the
|
||||
//! method names used here refer to the MonadPlus of sequences under
|
||||
//! concatenation. Several useful functions generalizing operations on
|
||||
//! sequences are included with this concept, like `append`, `prepend`
|
||||
//! and `filter`.
|
||||
//!
|
||||
//! @note
|
||||
//! This documentation does not go into much details about the nature
|
||||
//! of the MonadPlus concept. However, there is a nice Haskell-oriented
|
||||
//! [WikiBook][1] going into further details.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `concat` and `empty`
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! First, a MonadPlus is required to have a monoidal structure. Hence, it
|
||||
//! is no surprise that for any MonadPlus `M`, we require `M(T)` to be a
|
||||
//! valid monoid. However, we do not enforce that `M(T)` actually models
|
||||
//! the Monoid concept provided by Hana. Further, for all objects `a, b, c`
|
||||
//! of data type `M(T)`,
|
||||
//! @code
|
||||
//! // identity
|
||||
//! concat(empty<M(T)>(), a) == a
|
||||
//! concat(a, empty<M(T)>()) == a
|
||||
//!
|
||||
//! // associativity
|
||||
//! concat(a, concat(b, c)) == concat(concat(a, b), c)
|
||||
//! @endcode
|
||||
//!
|
||||
//! Secondly, a MonadPlus is also required to obey the following laws,
|
||||
//! which represent the fact that `empty<M(T)>()` must be some kind of
|
||||
//! absorbing element for the `chain` operation. For all objects `a` of
|
||||
//! data type `M(T)` and functions @f$ f : T \to M(U) @f$,
|
||||
//! @code
|
||||
//! chain(empty<M(T)>(), f) == empty<M(U)>()
|
||||
//! chain(a, always(empty<M(T)>())) == empty<M(U)>()
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! `Functor`, `Applicative` and `Monad`
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::optional`, `hana::tuple`
|
||||
//!
|
||||
//! [1]: https://en.wikibooks.org/wiki/Haskell/MonadPlus
|
||||
template <typename M>
|
||||
struct MonadPlus;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_MONAD_PLUS_HPP
|
||||
@@ -0,0 +1,101 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Monoid`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_MONOID_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_MONOID_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Monoid Monoid
|
||||
//! The `Monoid` concept represents data types with an associative
|
||||
//! binary operation that has an identity.
|
||||
//!
|
||||
//! Specifically, a [Monoid][1] is a basic algebraic structure typically
|
||||
//! used in mathematics to construct more complex algebraic structures
|
||||
//! like `Group`s, `Ring`s and so on. They are useful in several contexts,
|
||||
//! notably to define the properties of numbers in a granular way. At its
|
||||
//! core, a `Monoid` is a set `S` of objects along with a binary operation
|
||||
//! (let's say `+`) that is associative and that has an identity in `S`.
|
||||
//! There are many examples of `Monoid`s:
|
||||
//! - strings with concatenation and the empty string as the identity
|
||||
//! - integers with addition and `0` as the identity
|
||||
//! - integers with multiplication and `1` as the identity
|
||||
//! - many others...
|
||||
//!
|
||||
//! As you can see with the integers, there are some sets that can be
|
||||
//! viewed as a monoid in more than one way, depending on the choice
|
||||
//! of the binary operation and identity. The method names used here
|
||||
//! refer to the monoid of integers under addition; `plus` is the binary
|
||||
//! operation and `zero` is the identity element of that operation.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `plus` and `zero` satisfying the laws
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! For all objects `x`, `y` and `z` of a `Monoid` `M`, the following
|
||||
//! laws must be satisfied:
|
||||
//! @code
|
||||
//! plus(zero<M>(), x) == x // left zero
|
||||
//! plus(x, zero<M>()) == x // right zero
|
||||
//! plus(x, plus(y, z)) == plus(plus(x, y), z) // associativity
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`
|
||||
//!
|
||||
//!
|
||||
//! Free model for non-boolean arithmetic data types
|
||||
//! ------------------------------------------------
|
||||
//! A data type `T` is arithmetic if `std::is_arithmetic<T>::%value` is
|
||||
//! true. For a non-boolean arithmetic data type `T`, a model of `Monoid`
|
||||
//! is automatically defined by setting
|
||||
//! @code
|
||||
//! plus(x, y) = (x + y)
|
||||
//! zero<T>() = static_cast<T>(0)
|
||||
//! @endcode
|
||||
//!
|
||||
//! > #### Rationale for not making `bool` a `Monoid` by default
|
||||
//! > First, it makes no sense whatsoever to define an additive `Monoid`
|
||||
//! > over the `bool` type. Also, it could make sense to define a `Monoid`
|
||||
//! > with logical conjunction or disjunction. However, C++ allows `bool`s
|
||||
//! > to be added, and the method names of this concept really suggest
|
||||
//! > addition. In line with the principle of least surprise, no model
|
||||
//! > is provided by default.
|
||||
//!
|
||||
//!
|
||||
//! Structure-preserving functions
|
||||
//! ------------------------------
|
||||
//! Let `A` and `B` be two `Monoid`s. A function `f : A -> B` is said
|
||||
//! to be a [Monoid morphism][2] if it preserves the monoidal structure
|
||||
//! between `A` and `B`. Rigorously, for all objects `x, y` of data
|
||||
//! type `A`,
|
||||
//! @code
|
||||
//! f(plus(x, y)) == plus(f(x), f(y))
|
||||
//! f(zero<A>()) == zero<B>()
|
||||
//! @endcode
|
||||
//! Functions with these properties interact nicely with `Monoid`s, which
|
||||
//! is why they are given such a special treatment.
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Monoid
|
||||
//! [2]: http://en.wikipedia.org/wiki/Monoid#Monoid_homomorphisms
|
||||
template <typename M>
|
||||
struct Monoid;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_MONOID_HPP
|
||||
@@ -0,0 +1,187 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Orderable`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_ORDERABLE_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_ORDERABLE_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Orderable Orderable
|
||||
//! The `Orderable` concept represents totally ordered data types.
|
||||
//!
|
||||
//! Intuitively, `Orderable` objects must define a binary predicate named
|
||||
//! `less` returning whether the first argument is to be considered less
|
||||
//! than the second argument. The word "total" means that _distinct_
|
||||
//! objects must always be ordered; if `a` and `b` are not equal, then
|
||||
//! exactly one of `less(a, b)` and `less(b, a)` must be true. This is
|
||||
//! a contrast with weaker kinds of orders that would allow some objects
|
||||
//! to be incomparable (neither less than nor greater than). Also note
|
||||
//! that a non-strict total order may always be obtained from a strict
|
||||
//! total order (and vice-versa) by setting
|
||||
//! @code
|
||||
//! a <= b = !(b < a)
|
||||
//! a < b = !(b <= a)
|
||||
//! @endcode
|
||||
//! The non-strict version is used in the description of the laws because
|
||||
//! it makes them easier to parse for humans, but they could be formulated
|
||||
//! equivalently using the strict order.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `less`
|
||||
//!
|
||||
//! When `less` is defined, the other methods are defined from it using
|
||||
//! the same definition as mandated in the laws below.
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! Rigorously speaking, a [total order][1] `<=` on a set `S` is a binary
|
||||
//! predicate @f$ <= \;: S \times S \to bool @f$ such that for all
|
||||
//! `a`, `b`, `c` in `S`,
|
||||
//! @code
|
||||
//! if a <= b and b <= a then a == b // Antisymmetry
|
||||
//! if a <= b and b <= c then a <= c // Transitivity
|
||||
//! either a <= b or b <= a // Totality
|
||||
//! @endcode
|
||||
//! Additionally, the `less`, `greater` and `greater_equal` methods should
|
||||
//! have the following intuitive meanings:
|
||||
//! @code
|
||||
//! a < b if and only if !(b <= a)
|
||||
//! a > b if and only if b < a
|
||||
//! a >= b if and only if !(a < b)
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Refined concept
|
||||
//! ---------------
|
||||
//! 1. `Comparable` (free model)\n
|
||||
//! Since `Orderable` requires `less_equal` to be a total order, a model
|
||||
//! of `Comparable` may always be obtained by setting
|
||||
//! @code
|
||||
//! equal(x, y) = less_equal(x, y) && less_equal(y, x)
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`, `hana::optional`, `hana::pair`,
|
||||
//! `hana::string`, `hana::tuple`
|
||||
//!
|
||||
//!
|
||||
//! Free model for `LessThanComparable` data types
|
||||
//! ----------------------------------------------
|
||||
//! Two data types `T` and `U` that model the cross-type version of the
|
||||
//! usual [LessThanComparable][2] C++ concept are automatically a model
|
||||
//! of `Orderable` by setting
|
||||
//! @code
|
||||
//! less(x, y) = (x < y)
|
||||
//! @endcode
|
||||
//! The cross-type version of the LessThanComparable concept is analogous
|
||||
//! to the cross-type version of the EqualityComparable concept presented
|
||||
//! in [N3351][3], which is compatible with the usual single type
|
||||
//! definition.
|
||||
//! However, note that the LessThanComparable concept only requires `<`
|
||||
//! to be a [strict weak ordering][4], which is a weaker requirement
|
||||
//! than being a total order. Hence, if `less` is used with objects
|
||||
//! of a LessThanComparable data type that do not define a total order,
|
||||
//! some algorithms may have an unexpected behavior. It is the author's
|
||||
//! opinion that defining `operator<` as a non-total order is a bad idea,
|
||||
//! but this is debatable and so the design choice of providing a model
|
||||
//! for LessThanComparable data types is open to debate. Waiting for
|
||||
//! some user input.
|
||||
//!
|
||||
//!
|
||||
//! Order-preserving functions
|
||||
//! --------------------------
|
||||
//! Let `A` and `B` be two `Orderable` data types. A function
|
||||
//! @f$ f : A \to B@f$ is said to be order-preserving (also called
|
||||
//! monotone) if it preserves the structure of the `Orderable` concept,
|
||||
//! which can be rigorously stated as follows. For all objects `x`, `y`
|
||||
//! of data type `A`,
|
||||
//! @code
|
||||
//! if less(x, y) then less(f(x), f(y))
|
||||
//! @endcode
|
||||
//! Another important property is that of being order-reflecting, which
|
||||
//! can be stated as
|
||||
//! @code
|
||||
//! if less(f(x), f(y)) then less(x, y)
|
||||
//! @endcode
|
||||
//! We say that a function is an order-embedding if it is both
|
||||
//! order-preserving and order-reflecting, i.e. if
|
||||
//! @code
|
||||
//! less(x, y) if and only if less(f(x), f(y))
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Cross-type version of the methods
|
||||
//! ---------------------------------
|
||||
//! The comparison methods (`less`, `less_equal`, `greater` and
|
||||
//! `greater_equal`) are "overloaded" to handle distinct data types
|
||||
//! with certain properties. Specifically, they are defined for
|
||||
//! _distinct_ data types `A` and `B` such that
|
||||
//! 1. `A` and `B` share a common data type `C`, as determined by the
|
||||
//! `common` metafunction
|
||||
//! 2. `A`, `B` and `C` are all `Orderable` when taken individually
|
||||
//! 3. @f$\mathrm{to<C>} : A \to C@f$ and @f$\mathrm{to<C>} : B \to C@f$
|
||||
//! are both order-embeddings as determined by the `is_embedding`
|
||||
//! metafunction.
|
||||
//!
|
||||
//! The method definitions for data types satisfying the above
|
||||
//! properties are
|
||||
//! @code
|
||||
//! less(x, y) = less(to<C>(x), to<C>(y))
|
||||
//! less_equal(x, y) = less_equal(to<C>(x), to<C>(y))
|
||||
//! greater_equal(x, y) = greater_equal(to<C>(x), to<C>(y))
|
||||
//! greater(x, y) = greater(to<C>(x), to<C>(y))
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Partial application of the methods
|
||||
//! ----------------------------------
|
||||
//! The `less`, `greater`, `less_equal` and `greater_equal` methods can
|
||||
//! be called in two different ways. First, they can be called like
|
||||
//! normal functions:
|
||||
//! @code
|
||||
//! less(x, y)
|
||||
//! greater(x, y)
|
||||
//!
|
||||
//! less_equal(x, y)
|
||||
//! greater_equal(x, y)
|
||||
//! @endcode
|
||||
//!
|
||||
//! However, they may also be partially applied to an argument as follows:
|
||||
//! @code
|
||||
//! less.than(x)(y) == less(y, x)
|
||||
//! greater.than(x)(y) == greater(y, x)
|
||||
//!
|
||||
//! less_equal.than(x)(y) == less_equal(y, x)
|
||||
//! greater_equal.than(x)(y) == greater_equal(y, x)
|
||||
//! @endcode
|
||||
//!
|
||||
//! Take good note that the order of the arguments is reversed, so
|
||||
//! for example `less.than(x)(y)` is equivalent to `less(y, x)`, not
|
||||
//! `less(x, y)`. This is because those variants are meant to be used
|
||||
//! with higher order algorithms, where the chosen application order
|
||||
//! makes sense.
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Total_order
|
||||
//! [2]: http://en.cppreference.com/w/cpp/concept/LessThanComparable
|
||||
//! [3]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf
|
||||
//! [4]: http://en.wikipedia.org/wiki/Strict_weak_ordering
|
||||
template <typename Ord>
|
||||
struct Orderable;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_ORDERABLE_HPP
|
||||
@@ -0,0 +1,103 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Product`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_PRODUCT_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_PRODUCT_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Product Product
|
||||
//! Represents types that are generic containers of two elements.
|
||||
//!
|
||||
//! This concept basically represents types that are like `std::pair`.
|
||||
//! The motivation for making such a precise concept is similar to the
|
||||
//! motivation behind the `Sequence` concept; there are many different
|
||||
//! implementations of `std::pair` in different libraries, and we would
|
||||
//! like to manipulate any of them generically.
|
||||
//!
|
||||
//! Since a `Product` is basically a pair, it is unsurprising that the
|
||||
//! operations provided by this concept are getting the first and second
|
||||
//! element of a pair, creating a pair from two elements and other
|
||||
//! simmilar operations.
|
||||
//!
|
||||
//! @note
|
||||
//! Mathematically, this concept represents types that are category
|
||||
//! theoretical [products][1]. This is also where the name comes
|
||||
//! from.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `first`, `second` and `make`
|
||||
//!
|
||||
//! `first` and `second` must obviously return the first and the second
|
||||
//! element of the pair, respectively. `make` must take two arguments `x`
|
||||
//! and `y` representing the first and the second element of the pair,
|
||||
//! and return a pair `p` such that `first(p) == x` and `second(p) == y`.
|
||||
//! @include example/product/make.cpp
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! For a model `P` of `Product`, the following laws must be satisfied.
|
||||
//! For every data types `X` and `Y`, there must be a unique function
|
||||
//! @f$ \mathtt{make} : X \times Y \to P @f$ such that for every `x`, `y`,
|
||||
//! @code
|
||||
//! x == first(make<P>(x, y))
|
||||
//! y == second(make<P>(x, y))
|
||||
//! @endcode
|
||||
//!
|
||||
//! @note
|
||||
//! This law is less general than the universal property typically used to
|
||||
//! define category theoretical products, but it is vastly enough for what
|
||||
//! we need.
|
||||
//!
|
||||
//! This is basically saying that a `Product` must be the most general
|
||||
//! object able to contain a pair of objects `(P1, P2)`, but nothing
|
||||
//! more. Since the categorical product is defined by a universal
|
||||
//! property, all the models of this concept are isomorphic, and
|
||||
//! the isomorphism is unique. In other words, there is one and only
|
||||
//! one way to convert one `Product` to another.
|
||||
//!
|
||||
//! Another property that must be satisfied by `first` and `second` is
|
||||
//! that of @ref move-independence, which ensures that we can optimally
|
||||
//! decompose a `Product` into its two members without making redundant
|
||||
//! copies.
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! 1. `Comparable` (free model)\n
|
||||
//! Two products `x` and `y` are equal iff they are equal element-wise,
|
||||
//! by comparing the first element before the second element.
|
||||
//! @include example/product/comparable.cpp
|
||||
//!
|
||||
//! 2. `Orderable` (free model)\n
|
||||
//! Products are ordered using a lexicographical ordering as-if they
|
||||
//! were 2-element tuples.
|
||||
//!
|
||||
//! 3. `Foldable` (free model)\n
|
||||
//! Folding a `Product` `p` is equivalent to folding a list containing
|
||||
//! `first(p)` and `second(p)`, in that order.
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::pair`
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Product_(category_theory)
|
||||
template <typename P>
|
||||
struct Product;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_PRODUCT_HPP
|
||||
@@ -0,0 +1,106 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Ring`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_RING_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_RING_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Ring Ring
|
||||
//! The `Ring` concept represents `Group`s that also form a `Monoid`
|
||||
//! under a second binary operation that distributes over the first.
|
||||
//!
|
||||
//! A [Ring][1] is an algebraic structure built on top of a `Group`
|
||||
//! which requires a monoidal structure with respect to a second binary
|
||||
//! operation. This second binary operation must distribute over the
|
||||
//! first one. Specifically, a `Ring` is a triple `(S, +, *)` such that
|
||||
//! `(S, +)` is a `Group`, `(S, *)` is a `Monoid` and `*` distributes
|
||||
//! over `+`, i.e.
|
||||
//! @code
|
||||
//! x * (y + z) == (x * y) + (x * z)
|
||||
//! @endcode
|
||||
//!
|
||||
//! The second binary operation is often written `*` with its identity
|
||||
//! written `1`, in reference to the `Ring` of integers under
|
||||
//! multiplication. The method names used here refer to this exact ring.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definintion
|
||||
//! ----------------------------
|
||||
//! `one` and `mult` satisfying the laws
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! For all objects `x`, `y`, `z` of a `Ring` `R`, the following laws must
|
||||
//! be satisfied:
|
||||
//! @code
|
||||
//! mult(x, mult(y, z)) == mult(mult(x, y), z) // associativity
|
||||
//! mult(x, one<R>()) == x // right identity
|
||||
//! mult(one<R>(), x) == x // left identity
|
||||
//! mult(x, plus(y, z)) == plus(mult(x, y), mult(x, z)) // distributivity
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! `Monoid`, `Group`
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::integral_constant`
|
||||
//!
|
||||
//!
|
||||
//! Free model for non-boolean arithmetic data types
|
||||
//! ------------------------------------------------
|
||||
//! A data type `T` is arithmetic if `std::is_arithmetic<T>::%value` is
|
||||
//! true. For a non-boolean arithmetic data type `T`, a model of `Ring` is
|
||||
//! automatically defined by using the provided `Group` model and setting
|
||||
//! @code
|
||||
//! mult(x, y) = (x * y)
|
||||
//! one<T>() = static_cast<T>(1)
|
||||
//! @endcode
|
||||
//!
|
||||
//! @note
|
||||
//! The rationale for not providing a Ring model for `bool` is the same
|
||||
//! as for not providing Monoid and Group models.
|
||||
//!
|
||||
//!
|
||||
//! Structure-preserving functions
|
||||
//! ------------------------------
|
||||
//! Let `A` and `B` be two `Ring`s. A function `f : A -> B` is said to
|
||||
//! be a [Ring morphism][2] if it preserves the ring structure between
|
||||
//! `A` and `B`. Rigorously, for all objects `x, y` of data type `A`,
|
||||
//! @code
|
||||
//! f(plus(x, y)) == plus(f(x), f(y))
|
||||
//! f(mult(x, y)) == mult(f(x), f(y))
|
||||
//! f(one<A>()) == one<B>()
|
||||
//! @endcode
|
||||
//! Because of the `Ring` structure, it is easy to prove that the
|
||||
//! following will then also be satisfied:
|
||||
//! @code
|
||||
//! f(zero<A>()) == zero<B>()
|
||||
//! f(negate(x)) == negate(f(x))
|
||||
//! @endcode
|
||||
//! which is to say that `f` will then also be a `Group` morphism.
|
||||
//! Functions with these properties interact nicely with `Ring`s,
|
||||
//! which is why they are given such a special treatment.
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Ring_(mathematics)
|
||||
//! [2]: http://en.wikipedia.org/wiki/Ring_homomorphism
|
||||
template <typename R>
|
||||
struct Ring;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_RING_HPP
|
||||
@@ -0,0 +1,143 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Searchable`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_SEARCHABLE_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_SEARCHABLE_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Searchable Searchable
|
||||
//! The `Searchable` concept represents structures that can be searched.
|
||||
//!
|
||||
//! Intuitively, a `Searchable` is any structure, finite or infinite,
|
||||
//! containing elements that can be searched using a predicate. Sometimes,
|
||||
//! `Searchable`s will associate keys to values; one can search for a key
|
||||
//! with a predicate, and the value associated to it is returned. This
|
||||
//! gives rise to map-like data structures. Other times, the elements of
|
||||
//! the structure that are searched (i.e. those to which the predicate is
|
||||
//! applied) are the same that are returned, which gives rise to set-like
|
||||
//! data structures. In general, we will refer to the _keys_ of a
|
||||
//! `Searchable` structure as those elements that are used for searching,
|
||||
//! and to the _values_ of a `Searchable` as those elements that are
|
||||
//! returned when a search is successful. As was explained, there is no
|
||||
//! requirement that both notions differ, and it is often useful to have
|
||||
//! keys and values coincide (think about `std::set`).
|
||||
//!
|
||||
//! Some methods like `any_of`, `all_of` and `none_of` allow simple queries
|
||||
//! to be performed on the keys of the structure, while other methods like
|
||||
//! `find` and `find_if` make it possible to find the value associated
|
||||
//! to a key. The most specific method should always be used if one
|
||||
//! cares about performance, because it is usually the case that heavy
|
||||
//! optimizations can be performed in more specific methods. For example,
|
||||
//! an associative data structure implemented as a hash table will be much
|
||||
//! faster to access using `find` than `find_if`, because in the second
|
||||
//! case it will have to do a linear search through all the entries.
|
||||
//! Similarly, using `contains` will likely be much faster than `any_of`
|
||||
//! with an equivalent predicate.
|
||||
//!
|
||||
//! > __Insight__\n
|
||||
//! > In a lazy evaluation context, any `Foldable` can also become a model
|
||||
//! > of `Searchable` because we can search lazily through the structure
|
||||
//! > with `fold_right`. However, in the context of C++, some `Searchable`s
|
||||
//! > can not be folded; think for example of an infinite set.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `find_if` and `any_of`
|
||||
//!
|
||||
//! When `find_if` and `any_of` are provided, the other functions are
|
||||
//! implemented according to the laws explained below.
|
||||
//!
|
||||
//! @note
|
||||
//! We could implement `any_of(xs, pred)` by checking whether
|
||||
//! `find_if(xs, pred)` is an empty `optional` or not, and then reduce
|
||||
//! the minimal complete definition to `find_if`. However, this is not
|
||||
//! done because that implementation requires the predicate of `any_of`
|
||||
//! to return a compile-time `Logical`, which is more restrictive than
|
||||
//! what we have right now.
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! In order for the semantics of the methods to be consistent, some
|
||||
//! properties must be satisfied by any model of the `Searchable` concept.
|
||||
//! Rigorously, for any `Searchable`s `xs` and `ys` and any predicate `p`,
|
||||
//! the following laws should be satisfied:
|
||||
//! @code
|
||||
//! any_of(xs, p) <=> !all_of(xs, negated p)
|
||||
//! <=> !none_of(xs, p)
|
||||
//!
|
||||
//! contains(xs, x) <=> any_of(xs, equal.to(x))
|
||||
//!
|
||||
//! find(xs, x) == find_if(xs, equal.to(x))
|
||||
//! find_if(xs, always(false_)) == nothing
|
||||
//!
|
||||
//! is_subset(xs, ys) <=> all_of(xs, [](auto x) { return contains(ys, x); })
|
||||
//! is_disjoint(xs, ys) <=> none_of(xs, [](auto x) { return contains(ys, x); })
|
||||
//! @endcode
|
||||
//!
|
||||
//! Additionally, if all the keys of the `Searchable` are `Logical`s,
|
||||
//! the following laws should be satisfied:
|
||||
//! @code
|
||||
//! any(xs) <=> any_of(xs, id)
|
||||
//! all(xs) <=> all_of(xs, id)
|
||||
//! none(xs) <=> none_of(xs, id)
|
||||
//! @endcode
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::map`, `hana::optional`, `hana::range`, `hana::set`,
|
||||
//! `hana::string`, `hana::tuple`
|
||||
//!
|
||||
//!
|
||||
//! Free model for builtin arrays
|
||||
//! -----------------------------
|
||||
//! Builtin arrays whose size is known can be searched as-if they were
|
||||
//! homogeneous tuples. However, since arrays can only hold objects of
|
||||
//! a single type and the predicate to `find_if` must return a compile-time
|
||||
//! `Logical`, the `find_if` method is fairly useless. For similar reasons,
|
||||
//! the `find` method is also fairly useless. This model is provided mainly
|
||||
//! because of the `any_of` method & friends, which are both useful and
|
||||
//! compile-time efficient.
|
||||
//!
|
||||
//!
|
||||
//! Structure preserving functions
|
||||
//! ------------------------------
|
||||
//! Given two `Searchables` `S1` and `S2`, a function
|
||||
//! @f$ f : S_1(X) \to S_2(X) @f$ is said to preserve the `Searchable`
|
||||
//! structure if for all `xs` of data type `S1(X)` and predicates
|
||||
//! @f$ \mathtt{pred} : X \to Bool @f$ (for a `Logical` `Bool`),
|
||||
//! @code
|
||||
//! any_of(xs, pred) if and only if any_of(f(xs), pred)
|
||||
//! find_if(xs, pred) == find_if(f(xs), pred)
|
||||
//! @endcode
|
||||
//!
|
||||
//! This is really just a generalization of the following, more intuitive
|
||||
//! requirements. For all `xs` of data type `S1(X)` and `x` of data type
|
||||
//! `X`,
|
||||
//! @code
|
||||
//! x ^in^ xs if and only if x ^in^ f(xs)
|
||||
//! find(xs, x) == find(f(xs), x)
|
||||
//! @endcode
|
||||
//!
|
||||
//! These requirements can be understood as saying that `f` does not
|
||||
//! change the content of `xs`, although it may reorder elements.
|
||||
//! As usual, such a structure-preserving transformation is said to
|
||||
//! be an embedding if it is also injective, i.e. if it is a lossless
|
||||
//! transformation.
|
||||
template <typename S>
|
||||
struct Searchable;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_SEARCHABLE_HPP
|
||||
@@ -0,0 +1,165 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Sequence`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_SEQUENCE_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_SEQUENCE_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
#include <boost/hana/core/when.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Sequence Sequence
|
||||
//! The `Sequence` concept represents generic index-based sequences.
|
||||
//!
|
||||
//! Compared to other abstract concepts, the Sequence concept is very
|
||||
//! specific. It represents generic index-based sequences. The reason
|
||||
//! why such a specific concept is provided is because there are a lot
|
||||
//! of models that behave exactly the same while being implemented in
|
||||
//! wildly different ways. It is useful to regroup all those data types
|
||||
//! under the same umbrella for the purpose of generic programming.
|
||||
//!
|
||||
//! In fact, models of this concept are not only _similar_. They are
|
||||
//! actually _isomorphic_, in a sense that we define below, which is
|
||||
//! a fancy way of rigorously saying that they behave exactly the same
|
||||
//! to an external observer.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `Iterable`, `Foldable`, and `make`
|
||||
//!
|
||||
//! The `Sequence` concept does not provide basic methods that could be
|
||||
//! used as a minimal complete definition; instead, it borrows methods
|
||||
//! from other concepts and add laws to them. For this reason, it is
|
||||
//! necessary to specialize the `Sequence` metafunction in Hana's
|
||||
//! namespace to tell Hana that a type is indeed a `Sequence`. Explicitly
|
||||
//! specializing the `Sequence` metafunction can be seen like a seal
|
||||
//! saying "this data type satisfies the additional laws of a `Sequence`",
|
||||
//! since those can't be checked by Hana automatically.
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! The laws for being a `Sequence` are simple, and their goal is to
|
||||
//! restrict the semantics that can be associated to the functions
|
||||
//! provided by other concepts. First, a `Sequence` must be a finite
|
||||
//! `Iterable` (thus a `Foldable` too). Secondly, for a `Sequence` tag
|
||||
//! `S`, `make<S>(x1, ..., xn)` must be an object of tag `S` and whose
|
||||
//! linearization is `[x1, ..., xn]`. This basically ensures that objects
|
||||
//! of tag `S` are equivalent to their linearization, and that they can
|
||||
//! be created from such a linearization (with `make`).
|
||||
//!
|
||||
//! While it would be possible in theory to handle infinite sequences,
|
||||
//! doing so complicates the implementation of many algorithms. For
|
||||
//! simplicity, the current version of the library only handles finite
|
||||
//! sequences. However, note that this does not affect in any way the
|
||||
//! potential for having infinite `Searchable`s and `Iterable`s.
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! 1. `Comparable` (definition provided automatically)\n
|
||||
//! Two `Sequence`s are equal if and only if they contain the same number
|
||||
//! of elements and their elements at any given index are equal.
|
||||
//! @include example/sequence/comparable.cpp
|
||||
//!
|
||||
//! 2. `Orderable` (definition provided automatically)\n
|
||||
//! `Sequence`s are ordered using the traditional lexicographical ordering.
|
||||
//! @include example/sequence/orderable.cpp
|
||||
//!
|
||||
//! 3. `Functor` (definition provided automatically)\n
|
||||
//! `Sequence`s implement `transform` as the mapping of a function over
|
||||
//! each element of the sequence. This is somewhat equivalent to what
|
||||
//! `std::transform` does to ranges of iterators. Also note that mapping
|
||||
//! a function over an empty sequence returns an empty sequence and never
|
||||
//! applies the function, as would be expected.
|
||||
//! @include example/sequence/functor.cpp
|
||||
//!
|
||||
//! 4. `Applicative` (definition provided automatically)\n
|
||||
//! First, `lift`ing a value into a `Sequence` is the same as creating a
|
||||
//! singleton sequence containing that value. Second, applying a sequence
|
||||
//! of functions to a sequence of values will apply each function to
|
||||
//! all the values in the sequence, and then return a list of all the
|
||||
//! results. In other words,
|
||||
//! @code
|
||||
//! ap([f1, ..., fN], [x1, ..., xM]) == [
|
||||
//! f1(x1), ..., f1(xM),
|
||||
//! ...
|
||||
//! fN(x1), ..., fN(xM)
|
||||
//! ]
|
||||
//! @endcode
|
||||
//! Example:
|
||||
//! @include example/sequence/applicative.cpp
|
||||
//!
|
||||
//! 5. `Monad` (definition provided automatically)\n
|
||||
//! First, `flaten`ning a `Sequence` takes a sequence of sequences and
|
||||
//! concatenates them to get a larger sequence. In other words,
|
||||
//! @code
|
||||
//! flatten([[a1, ..., aN], ..., [z1, ..., zM]]) == [
|
||||
//! a1, ..., aN, ..., z1, ..., zM
|
||||
//! ]
|
||||
//! @endcode
|
||||
//! This acts like a `std::tuple_cat` function, except it receives a
|
||||
//! sequence of sequences instead of a variadic pack of sequences to
|
||||
//! flatten.\n
|
||||
//! __Example__:
|
||||
//! @include example/sequence/monad.ints.cpp
|
||||
//! Also note that the model of `Monad` for `Sequence`s can be seen as
|
||||
//! modeling nondeterminism. A nondeterministic computation can be
|
||||
//! modeled as a function which returns a sequence of possible results.
|
||||
//! In this line of thought, `chain`ing a sequence of values into such
|
||||
//! a function will return a sequence of all the possible output values,
|
||||
//! i.e. a sequence of all the values applied to all the functions in
|
||||
//! the sequences.\n
|
||||
//! __Example__:
|
||||
//! @include example/sequence/monad.types.cpp
|
||||
//!
|
||||
//! 6. `MonadPlus` (definition provided automatically)\n
|
||||
//! `Sequence`s are models of the `MonadPlus` concept by considering the
|
||||
//! empty sequence as the unit of `concat`, and sequence concatenation
|
||||
//! as `concat`.
|
||||
//! @include example/sequence/monad_plus.cpp
|
||||
//!
|
||||
//! 7. `Foldable`\n
|
||||
//! The model of `Foldable` for `Sequence`s is uniquely determined by the
|
||||
//! model of `Iterable`.
|
||||
//! @include example/sequence/foldable.cpp
|
||||
//!
|
||||
//! 8. `Iterable`\n
|
||||
//! The model of `Iterable` for `Sequence`s corresponds to iteration over
|
||||
//! each element of the sequence, in order. This model is not provided
|
||||
//! automatically, and it is in fact part of the minimal complete
|
||||
//! definition for the `Sequence` concept.
|
||||
//! @include example/sequence/iterable.cpp
|
||||
//!
|
||||
//! 9. `Searchable` (definition provided automatically)\n
|
||||
//! Searching through a `Sequence` is equivalent to just searching through
|
||||
//! a list of the values it contains. The keys and the values on which
|
||||
//! the search is performed are both the elements of the sequence.
|
||||
//! @include example/sequence/searchable.cpp
|
||||
//!
|
||||
//!
|
||||
//! Concrete models
|
||||
//! ---------------
|
||||
//! `hana::tuple`
|
||||
//!
|
||||
//!
|
||||
//! [1]: http://en.wikipedia.org/wiki/Isomorphism#Isomorphism_vs._bijective_morphism
|
||||
#ifdef BOOST_HANA_DOXYGEN_INVOKED
|
||||
template <typename S>
|
||||
struct Sequence;
|
||||
#else
|
||||
template <typename S, typename = void>
|
||||
struct Sequence : Sequence<S, when<true>> { };
|
||||
#endif
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_SEQUENCE_HPP
|
||||
@@ -0,0 +1,156 @@
|
||||
/*!
|
||||
@file
|
||||
Forward declares `boost::hana::Struct`.
|
||||
|
||||
@copyright Louis Dionne 2013-2017
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_HANA_FWD_CONCEPT_STRUCT_HPP
|
||||
#define BOOST_HANA_FWD_CONCEPT_STRUCT_HPP
|
||||
|
||||
#include <boost/hana/config.hpp>
|
||||
|
||||
|
||||
BOOST_HANA_NAMESPACE_BEGIN
|
||||
//! @ingroup group-concepts
|
||||
//! @defgroup group-Struct Struct
|
||||
//! The `Struct` concept represents `struct`-like user-defined types.
|
||||
//!
|
||||
//! The `Struct` concept allows restricted compile-time reflection over
|
||||
//! user-defined types. In particular, it allows accessing the names of
|
||||
//! the members of a user-defined type, and also the value of those
|
||||
//! members. `Struct`s can also be folded, searched and converted to
|
||||
//! some types of containers, where more advanced transformations can
|
||||
//! be performed.
|
||||
//!
|
||||
//! While all types can _in theory_ be made `Struct`s, only a subset of
|
||||
//! them are actually interesting to see as such. More precisely, it is
|
||||
//! only interesting to make a type a `Struct` when it is conceptually
|
||||
//! a C++ `struct`, i.e. a mostly dumb aggregate of named data. The way
|
||||
//! this data is accessed is mostly unimportant to the `Struct` concept;
|
||||
//! it could be through getters and setters, through public members,
|
||||
//! through non-member functions or it could even be generated on-the-fly.
|
||||
//! The important part, which is made precise below, is that those accessor
|
||||
//! methods should be move-independent.
|
||||
//!
|
||||
//! Another way to see a `Struct` is as a map where the keys are the names
|
||||
//! of the members and the values are the values of those members. However,
|
||||
//! there are subtle differences like the fact that one can't add a member
|
||||
//! to a `Struct`, and also that the order of the members inside a `Struct`
|
||||
//! plays a role in determining the equality of `Struct`s, which is not
|
||||
//! the case for maps.
|
||||
//!
|
||||
//!
|
||||
//! Minimal complete definition
|
||||
//! ---------------------------
|
||||
//! `accessors`
|
||||
//!
|
||||
//! A model of `Struct` is created by specifying a sequence of key/value
|
||||
//! pairs with the `accessors` function. The first element of a pair in
|
||||
//! this sequence represents the "name" of a member of the `Struct`, while
|
||||
//! the second element is a function which retrieves this member from an
|
||||
//! object. The "names" do not have to be in any special form; they just
|
||||
//! have to be compile-time `Comparable`. For example, it is common to
|
||||
//! provide "names" that are `hana::string`s representing the actual names
|
||||
//! of the members, but one could provide `hana::integral_constant`s just
|
||||
//! as well. The values must be functions which, when given an object,
|
||||
//! retrieve the appropriate member from it.
|
||||
//!
|
||||
//! There are several ways of providing the `accessors` method, some of
|
||||
//! which are more flexible and others which are more convenient. First,
|
||||
//! one can define it through tag-dispatching, as usual.
|
||||
//! @snippet example/struct.mcd.tag_dispatching.cpp main
|
||||
//!
|
||||
//! Secondly, it is possible to provide a nested `hana_accessors_impl`
|
||||
//! type, which should be equivalent to a specialization of
|
||||
//! `accessors_impl` for tag-dispatching. However, for a type `S`, this
|
||||
//! technique only works when the data type of `S` is `S` itself, which
|
||||
//! is the case unless you explicitly asked for something else.
|
||||
//! @snippet example/struct.mcd.nested.cpp main
|
||||
//!
|
||||
//! Finally, the most convenient (but least flexible) option is to use
|
||||
//! the `BOOST_HANA_DEFINE_STRUCT`, the `BOOST_HANA_ADAPT_STRUCT` or the
|
||||
//! `BOOST_HANA_ADAPT_ADT` macro, which provide a minimal syntactic
|
||||
//! overhead. See the documentation of these macros for details on how
|
||||
//! to use them.
|
||||
//!
|
||||
//! Also note that it is not important that the accessor functions retrieve
|
||||
//! an actual member of the struct (e.g. `x.member`). Indeed, an accessor
|
||||
//! function could call a custom getter or even compute the value of the
|
||||
//! member on the fly:
|
||||
//! @snippet example/struct.custom_accessor.cpp main
|
||||
//!
|
||||
//! The only important thing is that the accessor functions are
|
||||
//! move-independent, a notion which is defined below.
|
||||
//!
|
||||
//!
|
||||
//! @anchor move-independence
|
||||
//! Move-independence
|
||||
//! -----------------
|
||||
//! The notion of move-independence presented here defines rigorously
|
||||
//! when it is legitimate to "double-move" from an object.
|
||||
//!
|
||||
//! A collection of functions `f1, ..., fn` sharing the same domain is
|
||||
//! said to be _move-independent_ if for every fresh (not moved-from)
|
||||
//! object `x` in the domain, any permutation of the following statements
|
||||
//! is valid and leaves the `zk` objects in a fresh (not moved-from) state:
|
||||
//! @code
|
||||
//! auto z1 = f1(std::move(x));
|
||||
//! ...
|
||||
//! auto zn = fn(std::move(x));
|
||||
//! @endcode
|
||||
//!
|
||||
//! @note
|
||||
//! In the special case where some functions return objects that can't be
|
||||
//! bound to with `auto zk =` (like `void` or a non-movable, non-copyable
|
||||
//! type), just pretend the return value is ignored.
|
||||
//!
|
||||
//! Intuitively, this ensures that we can treat `f1, ..., fn` as
|
||||
//! "accessors" that decompose `x` into independent subobjects, and
|
||||
//! that do so without moving from `x` more than that subobject. This
|
||||
//! is important because it allows us to optimally decompose `Struct`s
|
||||
//! into their subparts inside the library.
|
||||
//!
|
||||
//!
|
||||
//! Laws
|
||||
//! ----
|
||||
//! For any `Struct` `S`, the accessors in the `accessors<S>()` sequence
|
||||
//! must be move-independent, as defined above.
|
||||
//!
|
||||
//!
|
||||
//! Refined concepts
|
||||
//! ----------------
|
||||
//! 1. `Comparable` (free model)\n
|
||||
//! `Struct`s are required to be `Comparable`. Specifically, two `Struct`s
|
||||
//! of the same data type `S` must be equal if and only if all of their
|
||||
//! members are equal. By default, a model of `Comparable` doing just that
|
||||
//! is provided for models of `Struct`. In particular, note that the
|
||||
//! comparison of the members is made in the same order as they appear in
|
||||
//! the `hana::members` sequence.
|
||||
//! @include example/struct/comparable.cpp
|
||||
//!
|
||||
//! 2. `Foldable` (free model)\n
|
||||
//! A `Struct` can be folded by considering it as a list of pairs each
|
||||
//! containing the name of a member and the value associated to that
|
||||
//! member, in the same order as they appear in the `hana::members`
|
||||
//! sequence. By default, a model of `Foldable` doing just that is
|
||||
//! provided for models of the `Struct` concept.
|
||||
//! @include example/struct/foldable.cpp
|
||||
//! Being a model of `Foldable` makes it possible to turn a `Struct`
|
||||
//! into basically any `Sequence`, but also into a `hana::map` by simply
|
||||
//! using the `to<...>` function!
|
||||
//! @include example/struct/to.cpp
|
||||
//!
|
||||
//! 3. `Searchable` (free model)\n
|
||||
//! A `Struct` can be searched by considering it as a map where the keys
|
||||
//! are the names of the members of the `Struct`, and the values are the
|
||||
//! members associated to those names. By default, a model of `Searchable`
|
||||
//! is provided for any model of the `Struct` concept.
|
||||
//! @include example/struct/searchable.cpp
|
||||
template <typename S>
|
||||
struct Struct;
|
||||
BOOST_HANA_NAMESPACE_END
|
||||
|
||||
#endif // !BOOST_HANA_FWD_CONCEPT_STRUCT_HPP
|
||||
Reference in New Issue
Block a user