Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Lambdas, Overloading and Pattern matching

0 views
Skip to first unread message

Mathias Gaunard

unread,
Oct 9, 2007, 2:09:13 PM10/9/07
to
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...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

int...@gmail.com

unread,
Oct 14, 2007, 3:40:12 AM10/14/07
to
On Oct 9, 10:09 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> 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?

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.

Mathias Gaunard

unread,
Oct 16, 2007, 1:44:29 AM10/16/07
to
On 14 oct, 09:40, int...@gmail.com wrote:

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

> <snip smart recursive inheritance />


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

Very nice syntax, and nice implementation too.

>
> 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.

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

int...@gmail.com

unread,
Oct 16, 2007, 12:25:17 PM10/16/07
to
On Oct 16, 9:44 am, Mathias Gaunard <loufo...@gmail.com> wrote:
> > 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.
>
> I don't really see how you can decompose signatures, could you
> elaborate?

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);

0 new messages