Given that @tclem and I have found the matcher API frustrating, I've
taken a stab at improving its ergonomics, and I've found some success
in separating composition of matchers from predicate-based narrowing
thereof.
The biggest change here is the elimination of the old `match`
combinator, which proved to be clumsy in that it complected narrowing
and composition. Top-down matching combinators are now written with
the `need` combinator and the `>>>` combinator, which is more readable
and more versatile. Here's a matcher that accepts functions with
Python docstrings:
```haskell
docstringMatcher :: ( Decl.Function :< fs
, [] :< fs
, Lit.TextElement :< fs
, term ~ Term (Sum fs) ann
) => Matcher term term
docstringMatcher = target <*
(need Decl.functionBody
>>> narrow @[]
>>> mhead
>>> narrow @Lit.TextElement
>>> ensure Lit.isTripleQuoted))
```
Pretty readable, right? Each step of the tree regular expression -
choosing function bodies, ensuring said bodies are lists, examining
the first element, and choosing only TextElements containing
triple-quoted strings - is made implicit. The old way would have
looked something like this:
```haskell
docstringMatcher = target <* match Decl.functionBody
$ narrow
$ matchM listToMaybe
$ target <* ensure Lit.isTripleQuoted
```
which is a good deal more disorganized and less flexible
in the quite-common case of applying functions during a
matching pass. Separating the act of composition from
function application is a big win here.
Further comments are inline.
As @robrix pointed out, adding explicit parenthesis nodes to our ASTs
bloats them with no added gain in expressivity. A pretty-printing
solution should use something analogous to `showsPrec` to ensure that
parentheses are printed properly.
Python is the only language that we support that permits multiple
inheritance, which is probably good, since MI is generally considered
a sign of a poorly-designed object hierarchy. But there's no reason
not to support it. This algorithm is more simplistic than Python's
actual method-resolution lookup, but it's fine for now, and the
behavior for simple cases matches that of Python.
`identifier` in the Python assignment module was too eager to reduce a
nested Attribute into a dotted identifier. Removing it fixes method
calls, which now have a unit test.