As I once again bemoaned D's lack of pattern matching yesterday, I was inspired to create this[0] implementation, that plays to D's strengths, allows for user-defined matching, and has a fairly usable syntax. The core usage looks like this:

unittest {
  auto a = tuple(1, "foo");
  auto b = match(a) (
    _!(int, "foo") = (int i) => 1,
    _!(_, _)       = ()      => 0
  );
  assert(b == 1);
}

With the user-defined matching implemented as follows:

struct Tuple(T...) {
   // Implementation

  // Magic happens here
  bool opMatch(Pattern, Args...)(Pattern p, ref Args args) {
    foreach (i, e; p.pattern) {
      static if (isTypeTuple!e) {
        enum n = countTypes!(p.pattern[0..i]);
        args[n] = fields[i];
      } else static if (!ignore!e) {
        if (fields[i] != e) {
          return false;
        }
      }
    }
  }
}

Or for Algebraic:

struct Algebraic(T...) {
  union {
    T fields;
  }
  size_t which;

bool opMatch(Pattern, Type)(Pattern p, ref Type args) if (staticIndexOf!(Type, T) > -1) {
    enum index = staticIndexOf!(Type, T);
    if (index == which) {
      args = fields[index];
      return true;
    }
    return false;
  }
}

The main problem I see is the temporary allocation of function arguments on line 124 and their assignment in opMatch, but I currently don't have a better solution.

Also, while I very much dislike using _ for an identifier, I feel it may be the best alternative here - it conveys the meaning of 'don't care' for the pattern, and doesn't stand out like a sore thumb before the exclamation mark. Other suggestions are welcome.

The code is available here, and I encourage everyone to play with it and critique:

[0]: https://github.com/Biotronic/Collectanea/blob/master/biotronic/pattern.d

Reply via email to