Class Pattern<T>
- Type Parameters:
T
- The type of the value that is pattern-matched
- Direct Known Subclasses:
Capture
,Patterns.Instance
public abstract class Pattern<T>
extends java.lang.Object
Capture
).
When implementing a pattern, the central method is apply
which takes a value value
(and a MatchManager
mgr
) and performs
the actual pattern matching. Inside apply
, we can perform the following operations:
- It can analyze and decompose
value
(or compute arbitrary derived values). - It can reject
value
by invokingreject()
. - It can invoke the
apply
function of other patterns on the values obtained by analyzingvalue
. (Typically, those would be given as arguments when constructing this pattern.) If any of the subpatterns fail, the this pattern fails, too (unless this is caught via a protected block (see below). - It can invoke
mgr
.protectedBlock
to execute a protected block of actions (see below).
Some important notes:
- There is no explicit mechanism for assigning capture variables. However,
capture variables are patterns themselves, and thus can be passed to a pattern as subpatterns.
If
x
is a capture, thenx.
will have the effect of assigning that capture.apply
(...) - Failure is marked via an exception
PatternMatchReject
(thrown byreject()
}. This exception should never be caught because subpatterns might have assigned values to some capture variables already and it would be undefined which capture variables would be assigned and which not. Instead, if we want to apply a subpattern but not fail if the subpattern fails, we need to execute the pattern in a protected block by invokingmgr.
. This protected block then returnsprotectedBlock
}(...)false
if the subpattern(s) in...
fail. - When invoking patterns that were passed as arguments when constructing this pattern,
it is recommended to do so in the order were given. This is to ensure that captures are assigned
in the right order. For example, if two patterns
p1
andp2
are given, andp1
assigns a capturex
, thenp2
may already read its assigned value. (E.g.,
will work butArray
(x,Is
(x))
will not.) If the patterns are invoked in a different order, this should be documented clearly.Array
(Is
(x), x) - If a patten fails, it does not matter at which point it does it. E.g.,
"
subpattern.
" and "apply
(...); if (...)reject()
if (...)
" are equivalent. (Unless the subpattern has any additional side-effects besides assigning captures.) All captures that were assigned by this pattern or subpatterns will be reset upon failure.reject()
; subpattern.apply
(...) - Patterns should not have any side-effects.
- When taking subpatterns as arguments, those should have types of the form
Pattern<? super X>
for some typeX
, notPattern<X>
. (This means,Pattern
should be treated as a contravariant type constructor.)
Example 1:
As an example for designing a pattern, consider the following use case. Assume a class Pair<X,Y>
with two members X first
and Y second
. We want to construct a pattern for this class. For this,
we create a static method (by convention, of the same name as Pair
)
that returns an anonymous subclass of Pattern<Pair<X,Y>>
:
Most of this is boilerplate. The crucial part are the three lines in the definition ofstatic <X, Y> Pattern<Pair<X, Y>> Pair(Pattern<? super X> firstPattern, Pattern<? super Y> secondPattern)
{return new Pattern<>()
{public void apply(MatchManager mgr, Pair<X, Y> value) throws PatternMatchReject
{ if (value == null) reject(); firstPattern.apply(mgr, value.first); secondPattern.apply(mgr, value.second); } public String toString() { return "Pair(" + firstPattern + "," + secondPattern + ")"; } }; }
apply
:
if (value == null) reject();
rejects the matched value if it is null
.
And firstPattern.apply(mgr, value.first);
matches the first component of the matched
value using the subpattern subpattern
. And analogously for the next line and the second component.
The full example can be found in
PatternDocumentationTest.java,
test case example1
.
Example 2: As an example for a pattern that does not correspond to an existing class such
as Pair
, we consider the following use case. We want a pattern that matches strings of
the form "firstname lastname" (and allows us to apply further subpatterns to the first and last name).
The following method achieves this:
Here thestatic Pattern<String> FullName(Pattern<String> firstNamePattern, Pattern<String> lastNamePattern)
{return new Pattern<>()
{ public void apply(MatchManager mgr, String value) throws PatternMatchReject { if (value == null) reject(); String[] parts = value.split(" "); if (parts.length != 2) reject(); firstNamePattern.apply(mgr, parts[0]); lastNamePattern.apply(mgr, parts[1]); } public String toString() { return "FullName(" + firstNamePattern + "," + lastNamePattern + ")"; } }; }
apply
method checks whether the matched value is non-null and consists of
two words (we do not handle names with more than two components) and rejects otherwise.
Then it applies the two subpatterns to the first and second word of the string, respectively.
The full example can be found in
PatternDocumentationTest.java,
test case example2
.
-
Constructor Summary
Constructors Constructor Description Pattern()
-
Method Summary
Modifier and Type Method Description abstract void
apply(@NotNull MatchManager mgr, T value)
Performs the pattern match.static <T> Capture<T>
capture(@NotNull java.lang.String name)
Creates a new capture variable.static void
reject()
Should be called insideapply(de.unruh.javapatterns.MatchManager, T)
to indicate that the pattern did not match.abstract java.lang.String
toString()
Should give a human readable representation of this pattern.
-
Constructor Details
-
Pattern
public Pattern()
-
-
Method Details
-
apply
public abstract void apply(@NotNull @NotNull MatchManager mgr, @Nullable T value) throws PatternMatchRejectPerforms the pattern match. See theclass documentation
.Must only be invoked from withing an
apply
. (I.e., a pattern may invokeapply
on its subpatterns from within its ownapply
method.)- Parameters:
mgr
- theMatchManager
that manages the life-cycle of the captures in this pattern match. Must be passed to subpatterns and not be kept after the termination of the invocation ofapply(...)
.value
- the value to be pattern-matched- Throws:
PatternMatchReject
- to indicate that value did not match the pattern. Should be thrown only by callingreject()
}.
-
toString
@Contract(pure=true) public abstract java.lang.String toString()Should give a human readable representation of this pattern. Used in error messages.- Overrides:
toString
in classjava.lang.Object
-
reject
Should be called insideapply(de.unruh.javapatterns.MatchManager, T)
to indicate that the pattern did not match.Can also be invoked inside a match action (the code that is executed if a pattern did match) to declare the match as failed. If that happens, the pattern match (via
Match.match(In, de.unruh.javapatterns.Case<In, Return, Exn>...)
) will continue with the next pattern.- Throws:
PatternMatchReject
- always thrown
-
capture
@Contract(pure=true, value="_ -> new") public static <T> Capture<T> capture(@NotNull @NotNull java.lang.String name)Creates a new capture variable.- Parameters:
name
- Name of the capture. Used only for informative purposes (printing patterns, error messages). It is recommended that this is the name of the variable holds this capture.- Returns:
- the capture variable
-