Lambdas, Overloading and Pattern matching

Lambdas, Overloading and Pattern matching

Post by Mathias Ga » Wed, 10 Oct 2007 21:09:13


boost.variant for example allows to visit a variant type through
overloading. This is very similar to sum types and pattern matching in
languages like ML.
However, this is quite verbose to write.

Lambdas, in their current form, do not take overloading into account,
and thus do not allow for nice and concise syntax to write different
kind of code depending on the type of their argument.
I am therefore wondering if anyone any thought of creating a new and
nicer syntax for that kind of thing?

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.yqcomputer.com/ ]
 
 
 

Lambdas, Overloading and Pattern matching

Post by int19 » Mon, 15 Oct 2007 10:40:12


It seems that it could be implemented as a library in C++0x:

// definition

namespace detail
{
template <class... Types>
struct common_type;

template <class T>
struct common_type<T>
{
typedef T result;
}

template <class T1, class T2>
struct common_type<T1, T2>
{
typedef decltype(true ? *static_cast<T1*>(0) :
*static_cast<T2*>(0)) result;
};

template <class T1, class T2, class... Types>
struct common_type<T1, T2, Types...>
{
typedef typename common_type<typename common_type<T1, T2>::result,
Types...>::result result;
};

template <class... Patterns>
struct matcher;

template <>
struct matcher<>
{
};

template <class Pattern, class... Patterns>
struct matcher<Pattern, Patterns...> : matcher<Patterns...>
{
Pattern pattern;

matcher(Pattern pattern, Patterns... patterns)
: pattern(pattern), matcher<Patterns...>(patterns...)
{
}

typename Pattern::result_type operator()(Pattern::argument_type&&
arg)
{
return pattern(arg);
}
};
}

template <class T, class... Patterns>
typename detail::common_type<Patterns::result_type...>::result
match(T&& value, Patterns... patterns)
{
detail::matcher<Patterns...>(patterns...)(value);
}


// usage

template <class T>
void foo(const T& x)
{
std::string type_name =
match(x,
<>(int x)("int")
<>(float x)("float")
<>(...)("unknown")
);
}

The above is a single example which only does pattern matching on
single values (but then so does boost::variant), and relies on the
presence of argument_type & result_type (but lambdas do provide that).
However, it seems to be quite possible to extend this to handle multi-
value patterns, and use decltype and variadic templates to decompose
signatures of function objects when argument_type & result_type are
not available, thus providing a fully generalised solution.

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.yqcomputer.com/ ]

 
 
 

Lambdas, Overloading and Pattern matching

Post by Mathias Ga » Wed, 17 Oct 2007 08:44:29


Very nice syntax, and nice implementation too.


I don't really see how you can decompose signatures, could you
elaborate?


---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.yqcomputer.com/ ]
 
 
 

Lambdas, Overloading and Pattern matching

Post by int19 » Wed, 17 Oct 2007 19:25:17


In the absence of concepts and particularly concept_maps, we only have
to consider two possibilities: plain function pointers, and classes
with overriden operator().
Both cases can be handled by applying decltype to a variadic template
function, letting it deduce the argument types:

// definition
template <class Return, class... Args>
std::tuple<Args...> extract_args(Return(*)(Args...)); //undefined

// usage
typedef decltype(extract_args(funcptr)) argument_types;


// definition
template <class T, class Return, class... Args>
std::tuple<Args...> extract_args(Return(T::*)(Args...)); //undefined
template <class Functor>
decltype(extract_args(&Functor::operator())) extract_args(Functor); //
undefined

// usage
typedef decltype(extract_args(funcobj)) argument_types;


With concepts, this won't work, since 1) operator() can be defined in
a concept_map, and 2) address of member function introduced via
concept cannot be taken. I wonder though if concepts themselves can be
used to deduce arguments? I.e.:

template <class Functor, class... Args>
requires Callable<Functor, Args...>
std::tuple<Args...> extract_args(Functor);


---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.yqcomputer.com/ ]