8000 GitHub - Hamdor/patterns: Pattern Matching in C++14
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Hamdor/patterns

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MPark.Patterns

Pattern Matching in C++14.

stability license wandbox

Introduction

MPark.Patterns is an experimental pattern matching library for C++14.

It determines whether a given value matches a pattern and, if it does, binds the desired portions of the value to a handler.

Pattern matching has been introduced to many programming languages outside of the functional world, and this library draws inspiration from languages such as Haskell, OCaml, Rust, Scala, and Swift.

#include <functional>
#include <iostream>
#include <sstream>

#include <mpark/match.hpp>

int eval(const std::string& equation) {
  std::istringstream strm(equation);
  strm.exceptions(std::istringstream::failbit);

  int lhs, rhs;
  std::string op;
  strm >> lhs >> op >> rhs;

  using namespace mpark;
  return match(lhs, op, rhs)(
      pattern(arg, "plus", arg) = std::plus<>{},
      pattern(arg, "minus", arg) = std::minus<>{},
      pattern(arg, "mult", arg) = std::multiplies<>{},
      pattern(arg, "div", arg) = std::divides<>{});
}

int main() {
  std::cout << eval("101 plus 202") << '\n';  // prints "303".
  std::cout << eval("64 div 2") << '\n';  // prints "32".
}

Basic Syntax

using namespace mpark;
match(<expr>...)(
  pattern(<pattern>...) = <handler>,
  pattern(<pattern>...) = <handler>,
  // ...
);

Types of Patterns

Wildcard Pattern

A wildcard pattern matches and ignores any value.

Requirements

None.

Syntax

  • _ (underscore)

Examples

int factorial(int n) {
  using namespace mpark;
  return match(n)(pattern(0) = [] { return 1; },
                  pattern(_) = [n] { return n * factorial(n - 1); });
}

Bind Pattern

A bind pattern passes any value matched by <pattern> to the handler.

Requirements

None.

Syntax

  • arg(<pattern>)
  • arg -- alias for arg(_)

Examples

int factorial(int n) {
  using namespace mpark;
  return match(n)(pattern(0) = [] { return 1; },
                  pattern(arg) = [](auto n) { return n * factorial(n - 1); });
}

Product Pattern

A product pattern matches values that holds multiple values.

Requirements

The type T satisfies Product if given a variable x of type T,

  • If std::tuple_size<T> is a complete type, x.get<I>() is valid for all I in [0, std::tuple_size<T>::value). Otherwise, get<I>(x) is valid for all I in [0, std::tuple_size<T>::value).
  • std::tuple_size<T>::value is a well-formed integer constant expression.

NOTE: These requirements are very similar to the requirements for C++17 Structured Bindings.

Syntax

  • prod(<pattern>...)

Examples

auto t = std::make_tuple(101, "hello", 1.1);

// C++17 Structured Bindings:
const auto& [x, y, z] = t;
// ...

// C++14 MPark.Patterns:
using namespace mpark;
match(t)(
    pattern(prod(arg, arg, arg)) = [](const auto& x, const auto& y, const auto& z) {
      // ...
    });

NOTE: The top-level is wrapped by a tuple, allowing us to write:

void fizzbuzz() {
  for (int i = 1; i <= 100; ++i) {
    using namespace mpark;
    match(i % 3, i % 5)(
        pattern(0, 0) = [] { std::cout << "fizzbuzz\n"; },
        pattern(0, _) = [] { std::cout << "fizz\n"; },
        pattern(_, 0) = [] { std::cout << "buzz\n"; },
        pattern(_, _) = [i] { std::cout << i << std::endl; });
  }
}

Sum Pattern

A sum pattern matches values that holds one of a set of alternatives.

The sum<T> pattern matches if the given value holds an instance of T. The sum pattern matches values of a sum type,

Requirements

The type T satisfies Sum<U> if given a variable x of type T,

  • If mpark::variant_size<T> is a complete type, x.get<U>() is valid. Otherwise, get<U>(x) is valid.
  • mpark::variant_size<T>::value is a well-formed integer constant expression.

The type T satisfies Sum if given a variable x of type T,

  • If mpark::variant_size<T> is a complete type, visit([](auto&&) {}, x) is valid.
  • mpark::variant_size<T>::value is a well-formed integer constant expression.

Syntax

  • sum<U>(<pattern>)
  • sum(<pattern>)

Examples

using str = std::string;
mpark::variant<int, str> v = 42;

using namespace mpark;
match(v)(pattern(sum<int>(_)) = [] { std::cout << "int\n"; },
         pattern(sum<str>(_)) = [] { std::cout << "str\n"; });
// prints "int".
using str = std::string;
mpark::variant<int, str> v = "hello world!";

struct {
  void operator()(int n) const { std::cout << "int: " << n << '\n'; }
  void operator()(const str& s) const { std::cout << "str: " << s << '\n'; }
} handler;

using namespace mpark;
match(v)(pattern(sum(arg)) = handler);
// prints: "str: hello world!".

Optional Pattern

An optional pattern matches values that can be dereferenced, and tested as a bool.

Requirements

The type T satisfies Optional if given a variable x of type T,

  • *x is a valid expression.
  • x is contextually convertible to bool.

Syntax

  • some(<pattern>)
  • none

Examples

int *p = nullptr;

using namespace mpark;
match(p)(pattern(some(_)) = [] { std::cout << "some\n"; },
         pattern(none) = [] { std::cout << "none\n"; });
// prints "none".
boost::optional<int> o = 42;

using namespace mpark;
match(o)(
    pattern(some(arg)) = [](auto x) { std::cout << "some(" << x << ")\n"; },
    pattern(none) = [] { std::cout << "none\n"; });
// prints "some(42)".

Variadic Pattern

A variadic pattern matches 0 or more values that match a given pattern.

Requirements

None.

Syntax

  • variadic(<pattern>)

Examples

auto x = std::make_tuple(101, "hello", 1.1);

using namespace mpark;
match(x)(
    pattern(prod(variadic(arg))) = [](const auto&... xs) {
      int dummy[] = { (std::cout << xs << ' ', 0)... };
      (void)dummy;
    });
// prints: "101 hello 1.1 "

This could also be used to implement C++17 std::apply:

template <typename F, typename Tuple>
decltype(auto) apply(F &&f, Tuple &&t) {
  using namespace mpark;
  return match(std::forward<T>(t))(
      pattern(prod(variadic(arg))) = std::forward<F>(f));
}

and even C++17 std::visit:

template <typename F, typename... Vs>
decltype(auto) visit(F &&f, Vs &&... vs) {
  using namespace mpark;
  return match(std::forward<Vs>(vs)...)(
      pattern(variadic(sum(arg))) = std::forward<F>(f));
}

We can even get a little fancier:

int x = 42;
auto y = std::make_tuple(101, "hello", 1.1);

using namespace mpark;
match(x, y)(
    pattern(arg, prod(variadic(arg))) = [](const auto&... xs) {
      int dummy[] = { (std::cout << xs << ' ', 0)... };
      (void)dummy;
    });
// prints: "42 101 hello 1.1 "

Alternation Pattern

An alternation pattern matches values that match any of the given patterns.

Requirements

None.

Syntax

  • anyof(<pattern>...)

Examples

std::string s = "large";

using namespace mpark;
match(s)(
    pattern(anyof("big", "large", "huge")) = [] { std::cout << "big!\n"; },
    pattern(anyof("little", "small", "tiny")) = [] { std::cout << "small.\n"; },
    pattern(_) = [] { std::cout << "unknown size.\n"; });

Pattern Guards

While pattern matching is used to match values against patterns and bind the desired portions, pattern guards are used to test whether the bound values satisfy some predicate.

Syntax

  • when(<condition>);

Examples

using namespace mpark;
match(101, 202)(
    pattern(arg, arg) = [](auto&& lhs, auto&& rhs) { when(lhs > rhs); std::cout << "GT\n"; },
    pattern(arg, arg) = [](auto&& lhs, auto&& rhs) { when(lhs < rhs); std::cout << "LT\n"; },
    pattern(arg, arg) = [](auto&& lhs, auto&& rhs) { when(lhs == rhs); std::cout << "EQ\n"; });
// prints "LT".

Related Work

About

Pattern Matching in C++14

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C++ 89.9%
  • CMake 10.1%
0