Alternative view patterns: lightweight views for Haskell

Basic view patterns

View patterns are a convenient way of pattern-matching against values of abstract types. For example, in a programming language implementation, we might represent the syntax of the types of the language as follows:

It is necessary to iterate the case, rather than using an equational function definition. And the situation is even worse when the matching against t is buried deep inside another pattern.
In response, programmers sometimes eschew type abstraction in favor of revealing a concrete datatype that is easy to pattern-match against.

View patterns permit calling the view function inside the pattern and matching against the result:

This has the same meaning as pattern guard at the top level, but here they are allowed to be part of a pattern, and thus nested inside other patterns.
So first the pattern is matched, then the qualifiers are matched in order. An expression qualifier has to evaluate to True, and for a pat <- exp the pattern must match the expression.

The key feature of this proposal is its modesty, rather than its ambition:

There is no new form of declaration (e.g. 'view' or 'pattern synonym').

No new syntax, the existing pattern guard syntax is simply generalized to be allowed inside patterns.

No changes are needed to import or export mechanisms.

Both static and dynamic semantics are extremely simple.

It is essentially some simple syntactic sugar for patterns.
However, sometimes modest syntactic sugar can have profound consequences. In this case, it's possible that people would start routinely hiding the data representation and exporting view functions instead, which would be an excellent thing.

Semantics

Scoping for pat | quals:

The variables bound by the view pattern are the variables bound by pat and quals.

Variables bound by patterns to the left of a view pattern expression are in scope. For example:

In function definitions, variables bound by matching earlier curried arguments may be used in view pattern expressions in later arguments.

This style should be discouraged when the view is in fact a total operation, as it moves the documentation of this fact out of the type system, making it harder for both people and the compiler to check exhaustiveness. However, it may be a useful idiom for defining a partial matching function with several constructors (e.g., in XML processing).

Sets

Here is a small module that allows to decompose sets with respect to a given element, deleting it hereby.

This parses 3 bits to get the value of n, and then parses n bits to get the value of val. Note that this example uses the left-to-right scoping in the inner tuple: the first component is jused in the view expression in the second.

n+k patterns are another a good opportunity for passing view data at run-time, as in:

example k (m | m > k) = ...

Named constants

View patterns can be used to pattern match against named constants:

errorVal :: Int -> Bool
errorVal = (== 4)
f (x | errorVar x) = ...

Both patterns

A "both pattern" pat1 & pat2 matches a value against both pat1 and pat2 and succeeds only when they both succeed. A special case is as-patterns, x@p, where the first pattern is a variable. Both patterns can be programmed using view patterns:

And used as follows:

f (x | xs <- x, h : t <- x) = h : (xs ++ t)

Comparison with existing view patterns

There are straightforward translations between old and new style view patterns:
pat<-exp
translates to
x | pat <- exp x where x is a fresh variable.