C++ Generic Match Function: Vicente - Botet@
C++ Generic Match Function: Vicente - Botet@
C++ Generic Match Function: Vicente - Botet@
Contents
Introduction.......................................................................................................................................... 1
Motivation and Scope...........................................................................................................................2
Tutorial................................................................................................................................................. 2
Customizing the match function...................................................................................................... 2
Using the match function to inspect one sum type.......................................................................... 3
Using the match function to visit several sum types....................................................................... 4
Design rationale....................................................................................................................................5
Result type of match ....................................................................................................................... 5
Multiple cases or overload............................................................................................................... 6
Grouping with overload .................................................................................................................. 6
Order of parameters and function name.......................................................................................... 6
const aware matchers....................................................................................................................... 7
Variadics...........................................................................................................................................7
Customization point......................................................................................................................... 8
Grouping with overload .................................................................................................................. 9
Considering any type as a sum type with a single alternative......................................................... 9
sum_type_alternatives................................................................................................................... 10
Open Points........................................................................................................................................ 10
Technical Specification.......................................................................................................................11
Header <experimental/functional> Synopsis................................................................................. 11
Header <experimental/optional> Synopsis....................................................................................13
Header <experimental/variant> Synopsis......................................................................................13
Implementation...................................................................................................................................14
Further work....................................................................................................................................... 14
Acknowledgements............................................................................................................................ 14
References.......................................................................................................................................... 14
Introduction
This paper presents a proposal for generic match functions that allow to visit sum types
individually or by groups (product of sum types). It is similar to boost::apply_visitor
1
Botet C++ generic match function P0050
[boost.variant] and std::experimental::visit from [N4542], but works for any model of
sum type.
Tutorial
Customizing the match function
The proposed match function works for sum types ST that have customized the following
overloaded function
template <class R, class F>
R match(ST const&, F&& );
In addition we need to know the sum type alternatives if we want to use match with several sum
types. This must be done by specializing the meta-function sum_type_alternatives as
follows
template <class ...Ts >
struct sum_type_alternatives<variant<Ts...>>
{
using type = ...;
}
2
Botet C++ generic match function P0050
std::experimental::variant<int, X> a = 2;
std::experimental::match(a,
[](int i)
{},
[](X const& i)
{
assert(false);
}
);
3
Botet C++ generic match function P0050
std::experimental::overload(
[](int i)
{},
[](X const& i)
{
assert(false);
}
));
The match function generalizes the visitation for several instances of heterogeneous sum types,
e.g. we could visit variant and optional at once:
std::experimental::variant<int, X> a = 2;
std::experimental::optional<int> b = 2;
std::experimental::match(std::tie_tuple(a, b),
[](int i, int j )
{
},
[](auto const &i, auto j )
{
assert(false);
}
);
Alternatively we could use a inspect factory that would wrap the tuple and provide two
functions match and first_match
std::experimental::inspect(a, b).first_match( // or match
[](int i, int j )
{
},
[](auto const &i, auto j )
{
assert(false);
}
);
4
Botet C++ generic match function P0050
Design rationale
Result type of match
We can consider several alternatives:
same type: the result type is the type returned by all the overloads (must be the same),
common_type: the result type is the common_type of the result of the overloads,
explicit return type R: the result type is R and the result type of the overloads must be
explicitly convertible to R,
For a sake of simplicity (and because our implementation needed the result type) this proposal only
contains a match version that uses the explicit return type. The others can be built on top of this
version.
Let Ri be the return type of the overloaded functor for the alternative i of the ST.
same_type_match: Check that all Ri are the same, let call it R, and only then call to
the explicit match<R>(...),
common_type_match: Let be R the common_type<Ri...> and only if it exists call
to the explicit match<R>(...).
Matt Calabrese has suggested a different approach:
1. If match is given an explicit result type, use that.
2. If match is NOT given an explicit result type, use the explicit result type of the function
object if one exists.
3. If neither match nor the function object have an explicit result type type, but all of the
invocations would have the same result type, use that.
4. If none of the above are true, then we have a few final options:
1. Produce a compile time error
2. Return void or some stateless type
3. Try to do some kind of common type deduction
To be optimal with compile-times, which is something that becomes a serious consideration with
variants having many states and/or doing n-ary visitation for n > 1, users would prefer option 1 or
option 2, since otherwise the implied checking, especially if common type deduction is at play,
could be considerable.
Here the explicit result type of the function stands for a nested result_type of the function object.
Note that [N4542] std::experimental::visit requires that all the overloads return the
5
Botet C++ generic match function P0050
same type.
This has the advantage of been orthogonal, we have a single match function (no need to make the
difference between match and first_match. The liability is much a question of style, we need
to type more.
6
Botet C++ generic match function P0050
Variadics
The two parameters of the match function can be variadic. We can have several sum types and
several functions overloading a possible match.
If we had a language type pattern matching feature (see [PM]) the authors guess it would be
something like:
boost::variant<int, X, float> a = 2;
boost::optional<int> b = 2;
match (a, b) {
case (int i, int j ) :
//...
case (int i, auto j ) :
//...
default:
assert(false);
}
The sum types would be grouped in this case using the match (a, b) and the cases would be the
variadic part. The cases would be matched sequentially.
This is a major motivation to place the sum type variables as the first parameter and let the matchers
variadic since the second parameter.
7
Botet C++ generic match function P0050
In addition, the match statement would allow to const-aware cases on some of the types
This is not possible with the variadic interface proposed in [ N4542] as all the variants must be
either const or not const. However the multiple sum types uses a const tuple as parameter, but the
tuple types can be const and non-const and can contain references or not depending on whether we
use std::make_tuple or std::tie_tuple.
Customization point
A customization point must be defined for any sum type ST as an overload of this function
template <class ST, class F>
match(ST & st, F&& f);
It is required that any customization respect the following, but it can be more restrictive.
Requires: The invocation expression of f with for all the alternative types of the sum type ST must
be a valid expression.
Effects: Calls f with the current contents of the sum type.
Throws: doesn't throws any other exception that the invocation of the callable.
Remark: While the std::experimental::visit function proposed in [] requires the
invocation of the callable must be implemented in O(1), i.e. it must not depend on
sum_type_size<ST>, this proposal suggest to leverage this requirement and considered it as a
8
Botet C++ generic match function P0050
QOI.
This has the advantage of been orthogonal, we have a single match function (no need to have match
and first_match. The liability is much a question of style, we need to type more.
This seems not natural as we are able to use directly the variable a inside each match case.
int a = 2;
boost::optional<int> b = 2;
match (b) {
case (int j ) :
//... make use of a and j
default:
assert(false);
}
Using the library solution each case is represented by a function and the function would not have
direct access to the variable a
int a = 2;
boost::optional<int> b = 2;
auto x = inspect(b).match(
[a] (int j ) {
return sum(a,j);
},
9
Botet C++ generic match function P0050
Allowing types with a single alternative makes the code more homogeneous
int a = 2;
boost::optional<int> b = 2;
inspect(a, b).match(
[] (int i, int j) {
return sum(i,j);
},
[] (auto const &j ) :
assert(false);
[](...) {
assert(false);}
);
sum_type_alternatives
[N4542] variant proposal specialize tuple_element and tuple_size for
variant<Types...>. The author think that as variant is not a product type, specializing these
function is weird. However the alternatives of a sum type are a product type and so we can
specialize tuple_element and tuple_size on
sum_type_alternatives<ST>::type.
The current implementation uses a patter matching approach and it requires just
sum_type_alternatives<ST>::type to be a variadic template instantiation. As for
example
Note that his proposal doesn't proposes this class, this is just an example.
Open Points
The authors would like to have an answer to the following points if there is at all an interest in this
proposal:
Which strategy for the match return type?
Which complexity for the customization point?
match versus visit
N proposes a visit function to visit variants that takes the arguments in the reverse order.
What do we prefer visit or match?
Which order of arguments do we prefer?
10
Botet C++ generic match function P0050
Technical Specification
Header <experimental/functional> Synopsis
namespace std {
namespace experimental {
inline namespace fundamental_v2 {
}}}
11
Botet C++ generic match function P0050
Remarks: This function will not participate in overload resolution if ST is a tuple type.
Throws: Any exception thrown during the construction any internal object or thrown by the call of
the selected overloaded function.
template <class R, class... STs, class... Fs>
'see below' match(const std::tuple<STs...> &those, Fs &&... fcts);
It is required that any customization respect the following, but it can be more restrictive.
Requires: The invocation expression of f with for all the alternative types of the sum type ST must
be a valid expression.
Effects: Calls f with the current contents of the sum type.
12
Botet C++ generic match function P0050
Throws: doesn't throws any other exception that the invocation of the callable.
Remark: While the std::experimental::visit function proposed in [N4542] requires the
invocation of the callable must be implemented in O(1), i.e. it must not depend on
sum_type_size<ST>, this proposal suggest to leverage this requirement and considered it as a
QOI.
Note: If we accept that any type can be seen as a sum type with it as single alternative we can add
the following overload:
13
Botet C++ generic match function P0050
Implementation
There is an implementation at https://github.com/viboes/tags including a customization for
boost::variant and std::experimental::optional.
Further work
The multiple sum type matching not only can be used with tuples created with
std::make_tuple, we should be able to use also std::tie_tuple and
std::forward_as_tuple.
Acknowledgements
Many thanks to Matt Calabrese for its insightful suggestions on the result type approach.
References
[boost.variant] apply_visitor
http://www.boost.org/doc/libs/1_59_0/doc/html/variant/reference.html#header.boost.variant.
apply_visitor_hpp
[N4542] N4542 - Variant: a type-safe union (v4) http://www.open-
std.org/jtc1/sc22/wg21/docs/papers/2015/n4542.pdf
[N3915] N3915 - apply() call a function with arguments from a tuple (V3)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3915.pdf
[P0051] P0051 - C++ generic overload function
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0051r0.pdf
[PM] Open Pattern Matching for C++
http://www.stroustrup.com/OpenPatternMatching.pdf
14