208 lines
No EOL
18 KiB
HTML
208 lines
No EOL
18 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<title>CLiki: fare-matcher-docs</title>
|
|
<link rel="alternate" type="application/atom+xml" title="ATOM feed of edits to current article"
|
|
href="https://www.cliki.net/site/feed/article.atom?title=fare-matcher-docs">
|
|
<link rel="stylesheet" href="static/css/style.css">
|
|
<link rel="stylesheet" href="static/css/colorize.css">
|
|
</head>
|
|
|
|
<body>
|
|
<span class="hidden">CLiki - fare-matcher-docs</span>
|
|
<div id="content"><div id="content-area"><div id="article-title">fare-matcher-docs</div><div id="article"><a href="Fare-matcher.html" class="category">Fare-matcher</a> adds constructs for Lisp2 <a href="pattern matching.html" class="internal">pattern matching</a> or destructuring.<p>Fare-matcher was superseded by <a href="optima.html" class="internal">optima</a> according to optima's CLiki page. However, the discussion below is of general interest and importance. Lots of it is relevant for libraries that had superseded fare-matcher as they inherited some of its design. Note also the Lisp-2 vs Lisp-1 bit.<p><hr><p>This <a href="language extension.html" class="internal">language extension</a> uses Lisp-2 style, which means that instead of writing
|
|
<div class="code"><span class="nonparen"><span class="paren1">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/mac_destructuring-bind.html" class="symbol">destructuring-bind</a>
|
|
<span class="paren2">(<span class="nonparen">a b <span class="paren3">(<span class="nonparen">c d <span class="paren4">(<span class="nonparen"></span>)</span></span>)</span> . e</span>)</span>
|
|
foo
|
|
<span class="paren2">(<span class="nonparen">bar a b c d e</span>)</span></span>)</span></span></div><p>you'd write
|
|
<div class="code"><span class="nonparen"><span class="paren1">(<span class="nonparen">letm
|
|
<span class="paren2">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/fun_listcm_listst.html" class="symbol">list*</a> a b <span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_list.html" class="symbol">list</a> c d <span class="paren4">(<span class="nonparen"></span>)</span></span>)</span> e</span>)</span>
|
|
foo
|
|
<span class="paren2">(<span class="nonparen">bar a b c d e</span>)</span></span>)</span></span></div><p>Lisp-2 style means that instead of writing
|
|
(destructuring-bind (a b (c d ()) . e) foo (bar a b c d e))
|
|
You'd write
|
|
(match1 (list* a b (list c d 'nil) e) foo (bar a b c d e))
|
|
This might look heavier, but then, the fact that the head of a pattern
|
|
is a designator for an arbitrary pattern instead of having patterns
|
|
always be lists except for possible "magic" values like &rest and
|
|
other keywords allows patterns to be clearer, and *extensible*.
|
|
Thus you can trust that any symbol in head position is a pattern name,
|
|
while any symbol in parameter position is a variable name,
|
|
except if it is a special symbol, or if the head is a pattern macro,
|
|
in which case it controls the meaning of the parameters.<p>The only predefined special symbol is _ that matches everything.
|
|
I used to have T match anything (top) and NIL match nothing (bottom),
|
|
but nobody liked it, so instead they are considered (together with keywords)
|
|
as literals that match the constants T and NIL.
|
|
Predefine functional patterns are CONS, LIST, LIST*, VECTOR,
|
|
that match corresponding objects with a known arity.
|
|
Predefined macro patterns are QUOTE, VALUE, LIKE-WHEN, AND, WHEN, OF-TYPE,
|
|
SLOT*, ACCESSOR*, that respectively match a literal constant, the value
|
|
of an expression, a pattern with a guard condition, the conjunction of
|
|
several patterns, just a guard condition, any value of given type,
|
|
any object with slots as specified, and object with accessors as specified.
|
|
In fare-clos, it is also possible to match against an INSTANCE of a class
|
|
with slots specified by initargs.<p>IFMATCH tries to match a pattern with an expression,
|
|
and conditionally executes either the success form
|
|
in a properly extended lexical environment,
|
|
or the failure form in the original lexical environment,
|
|
depending on whether the match succeeded (with freshly bound variables) or not.
|
|
MATCH (that I won't rename to COND-MATCH) tries to match a given expression
|
|
with several patterns, and executes the body of the matching clause if found.
|
|
EMATCH is like MATCH except that when no match is found,
|
|
it raises an error instead of returning NIL.<p>With this paradigm, matching patterns are thus dual from normal forms.
|
|
I like to think of all forms as patterns, with some patterns being
|
|
in "deconstruction position" (left-hand side of a match clause),
|
|
and other patterns being in "construction position" (right-hand side
|
|
of a match clause).
|
|
Although the current implementation follows Erlang (or ML-like) semantics
|
|
for matching, this paradigm can generalize to non-deterministic settings,
|
|
where you'd obtain something much like Mercury, unifying functional
|
|
and logic programming -- however, I haven't even attempted to implement
|
|
non-determinism (maybe this could be done using Screamer).<p>NB: Actually, I had first thought about this pattern-matcher when I was more
|
|
of a Lisp-1 fan, and the fact that Lisp-2 was much more natural for the pattern
|
|
matcher finished to turn me into a Lisp-2 fan.
|
|
<hr><p>With fare-matcher, you can use these macros in your code:<p>
|
|
<b>ifmatch</b> <pattern> <form> <then-clause> <else-clause><p><div class="code"><span class="nonparen"> <span class="paren1">(<span class="nonparen">ifmatch <span class="paren2">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_cons.html" class="symbol">cons</a> a b</span>)</span> '<span class="paren2">(<span class="nonparen">1</span>)</span> <span class="paren2">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_list.html" class="symbol">list</a> a b</span>)</span> -> <span class="paren2">(<span class="nonparen">1 <a href="https://www.cliki.net/site/HyperSpec/Body/any_nil.html" class="symbol">nil</a></span>)</span></span></span></span></div><p> <b>ematch</b> <form> <case-clauses>
|
|
<b>match</b> <form> <case-clauses><p> <div class="code"><span class="nonparen"> <span class="paren1">(<span class="nonparen">match msg
|
|
<span class="paren2">(<span class="nonparen"><span class="keyword">:ping</span> <span class="paren3">(<span class="nonparen">reply <span class="keyword">:pong</span></span>)</span></span>)</span>
|
|
<span class="paren2">(<span class="nonparen"><span class="paren3">(<span class="nonparen"><span class="keyword">:add</span> a b</span>)</span> <span class="paren3">(<span class="nonparen">reply `<span class="paren4">(<span class="nonparen">result ,<span class="paren5">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_pl.html" class="symbol">+</a> a b</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="nonparen"><span class="keyword">:quit</span> <span class="paren4">(<span class="nonparen">reply <span class="keyword">:bye</span></span>)</span></span>)</span></span>)</span></span></span></span></div><p> <b>letm</b> <pattern> <form> <lexical-scoped-body><p><div class="code"><span class="nonparen"><span class="paren1">(<span class="nonparen">letm <span class="paren2">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_values.html" class="symbol">values</a> <span class="paren3">(<span class="nonparen">accessor* <span class="paren4">(<span class="nonparen"><span class="paren5">(<span class="nonparen">like-when msg <span class="paren6">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/fun_keywordp.html" class="symbol">keywordp</a> msg</span>)</span></span>)</span> command</span>)</span></span>)</span>
|
|
err?</span>)</span> <span class="paren2">(<span class="nonparen">read-command</span>)</span>
|
|
<span class="paren2">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/speope_if.html" class="symbol"><i><span class="symbol">if</span></i></a> err?
|
|
<span class="string">"ouch"</span>
|
|
<span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_list.html" class="symbol">list</a> msg</span>)</span></span>)</span></span>)</span>
|
|
</span></div>
|
|
There are others.<p>Patterns are lisp forms. Symbol in patterns match anything creating a binding to what they match, except <b>*</b> or <b>_</b> will match anything but are not bound. Literals are matched using eq.<p>The core pattern matching forms are look like the forms for consing up things and match what otherwise they would create: <b>list</b>, <b>list*</b>, <b>values</b>, <b>cons</b>, and <b>vector</b>. Quite concise is that for most common lisp implementations you may use back-quoted forms.<p><div class="code"><span class="nonparen"><span class="paren1">(<span class="nonparen">match msg
|
|
<span class="paren2">(<span class="nonparen">`<span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_pl.html" class="symbol">+</a> ,a ,b</span>)</span> <span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_pl.html" class="symbol">+</a> a b</span>)</span></span>)</span>
|
|
<span class="paren2">(<span class="nonparen">`<span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_-.html" class="symbol">-</a> ,a ,b</span>)</span> <span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_-.html" class="symbol">-</a> a b</span>)</span></span>)</span>
|
|
<span class="paren2">(<span class="nonparen">`<span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_-.html" class="symbol">-</a> ,a</span>)</span> <span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_-.html" class="symbol">-</a> a b</span>)</span></span>)</span></span>)</span>
|
|
</span></div><p>Two forms <b>slot*</b>, <b>accessor*</b> allow you to match CLOS objects using the slot-names or the accessors respectively. By example:
|
|
<div class="code"><span class="nonparen"><span class="paren1">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/mac_defun.html" class="symbol"><i><span class="symbol">defun</span></i></a> window-hieght <span class="paren2">(<span class="nonparen">window</span>)</span>
|
|
<span class="paren2">(<span class="nonparen">letm <span class="paren3">(<span class="nonparen">slot* <span class="paren4">(<span class="nonparen">top a</span>)</span> <span class="paren4">(<span class="nonparen">bottom b</span>)</span></span>)</span> window
|
|
<span class="paren3">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_-.html" class="symbol">-</a> bottom top</span>)</span></span>)</span></span>)</span>
|
|
</span></div>
|
|
Finally a few pattern forms provide for special cases: <b>and</b>, <b>or</b>, <b>of-type</b>, <b>like-when</b>.<p>
|
|
Some samples pulled from a test case:<p><pre>
|
|
(ifmatch (cons * (cons x y)) '(1 (2 3) 4) (list x y)) ==> ((2 3) (4))
|
|
|
|
(ifmatch (like-when (cons x y) (eql x y)) '(2 . 2) x) ==> 2
|
|
|
|
(ifmatch (and (cons x y) (when (eql x y))) '(2 . 2) x) ==> 2
|
|
|
|
(ifmatch (and (cons a 1) (cons 2 b)) '(2 . 1) (list a b)) ==> (2 1)
|
|
|
|
(ifmatch (list a b) '(1 2) (list b a)) ==> (2 1)
|
|
|
|
(ifmatch (list* a b c) '(1 2 3 4) (list a b c)) ==> (1 2 (3 4))
|
|
|
|
(ifmatch (and x (cons (of-type integer) *)) '(2) x) => (2)
|
|
</pre><p> <div class="code"><span class="nonparen"><span class="paren1">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/mac_defun.html" class="symbol"><i><span class="symbol">defun</span></i></a> my-length <span class="paren2">(<span class="nonparen">x</span>)</span>
|
|
<span class="paren2">(<span class="nonparen">ematch x
|
|
<span class="paren3">(<span class="nonparen"><span class="paren4">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/any_cons.html" class="symbol">cons</a> <a href="https://www.cliki.net/site/HyperSpec/Body/any_st.html" class="symbol">*</a> tail</span>)</span> <span class="paren4">(<span class="nonparen"><a href="https://www.cliki.net/site/HyperSpec/Body/fun_1plcm_1-.html" class="symbol">1+</a> <span class="paren5">(<span class="nonparen">my-length tail</span>)</span></span>)</span></span>)</span>
|
|
<span class="paren3">(<span class="nonparen">'<a href="https://www.cliki.net/site/HyperSpec/Body/any_nil.html" class="symbol">nil</a> 0</span>)</span></span>)</span>
|
|
</span></span></span></div>
|
|
<pre>
|
|
(my-length '(1 2 3)) ==> 3
|
|
</pre><p><p>Finally, from the source code:<p>This is my attempt at a pattern matcher.<p>Semantic Design goals:
|
|
<ul>
|
|
<li>ML- or Erlang-like semantics for pattern matching.</li>
|
|
<li>Integration to the lexical environment:
|
|
Variables in a match pattern are bound lexically around the form's body.</li>
|
|
<li>Unlike destructuring-bind, follow the same Lisp-2 paradigm
|
|
of source-level lists specifying a generic pattern to call and its arguments.
|
|
This also allows to trivially distinguish between variables and pattern names
|
|
by a simple syntactic positional criterion.</li>
|
|
<li>Extensibility through definition of new generic patterns,
|
|
either functions or macros, by defining new names
|
|
or having algebraic designators.</li>
|
|
<li>No backtracking or unification,
|
|
but leaving possibility for such thing as an extension.</li>
|
|
</ul><p>Implementation goals:
|
|
<ul>
|
|
<li>macro-expansion-time transformation of pattern into recognizing code.</li>
|
|
<li>This first version is no frills:
|
|
no attempt at algorithmic efficiency, optimization or backtracking.</li>
|
|
<li>Optimized for human simplicity.</li>
|
|
<li>Highly-factored using higher-order functions and macros.</li>
|
|
<li> Underlying control structures are to be abstract enough
|
|
so that backtracking could be added by replacing them.</li>
|
|
</ul>
|
|
Implementation choices:
|
|
<ul>
|
|
<li> the (ifmatch pattern form body...) macro expands into a
|
|
(let (,@vars) (when (funcall ,matcher ,form) ,@body))
|
|
where (pattern-matcher pattern) returns (values matcher vars)</li>
|
|
<li> matching code uses matcher-passing style - that's good.</li>
|
|
<li> macro-expansion code uses a direct synthesis technique - that's bad.
|
|
It means that all bindings for the match are common to all the matching forms,
|
|
which requires a bit of discipline from writers of LIKE-WHEN clauses
|
|
so that nothing evil should happen.
|
|
Instead, we should use a monadic lexical-environment-passing-style
|
|
that would create bindings as the match progresses.</li>
|
|
<li>not much of any error detection is done,
|
|
and when there is, error reporting is minimal.</li>
|
|
<li>not any pattern optimization is done by the matching macros (however,
|
|
since patterns are expanded into lisp code at macro-expansion time,
|
|
the compiler might still do a few optimizations and produce reasonable code).</li>
|
|
</ul>
|
|
To Do list:
|
|
<ul>
|
|
<li>add a special case for (values) at the top level (???)</li>
|
|
<li>add better patterns for matching structures and classes</li>
|
|
<li>add macros for defining simple patterns of patterns,
|
|
both in matching and in building.</li>
|
|
<li>add lots of error checking</li>
|
|
<li>make for a better propagation of bindings within internal clauses of
|
|
the pattern matcher (due to like-when). -- binding-passing style(?)</li>
|
|
<li>add non-linearity???</li>
|
|
<li>add backtracking, based on a CPS transform??? Or use Screamer?</li>
|
|
<li>add first-class patterns (?)</li>
|
|
<li>add pattern merging and optimization (!?) or maybe declarations?
|
|
Factor the matching of identical initial patterns.</li>
|
|
<li>add better documentation about the pattern matcher (???)</li>
|
|
<li>add support for documentation strings in pattern defining forms</li>
|
|
<li>add support for optional, rest and keyword arguments in defining forms</li>
|
|
<li>add reader-macros for very common cases, if needed.</li>
|
|
<li>have the equivalent of compiler-macros so as to define patterns to use
|
|
when the patterns match a known pattern. E.g. when appending something
|
|
to a list of known length (before or after), the matching procedure is simple.</li>
|
|
<li>convert everything for use with some kind of first-class environments.</li>
|
|
</ul></div></div>
|
|
<div id="footer" class="buttonbar"><ul><li><a href="fare-matcher-docs.html">Current version</a></li>
|
|
<li><a href="https://www.cliki.net/site/history?article=fare-matcher-docs">History</a></li>
|
|
<li><a href="https://www.cliki.net/site/backlinks?article=fare-matcher-docs">Backlinks</a></li><li><a href="https://www.cliki.net/site/edit-article?title=fare-matcher-docs&from-revision=3822326447">Edit</a></li><li><a href="https://www.cliki.net/site/edit-article?create=t">Create</a></li></ul></div>
|
|
</div>
|
|
<div id="header-buttons" class="buttonbar">
|
|
<ul>
|
|
<li><a href="https://www.cliki.net/">Home</a></li>
|
|
<li><a href="https://www.cliki.net/site/recent-changes">Recent Changes</a></li>
|
|
<li><a href="CLiki.html">About</a></li>
|
|
<li><a href="Text Formatting.html">Text Formatting</a></li>
|
|
<li><a href="https://www.cliki.net/site/tools">Tools</a></li>
|
|
</ul>
|
|
<div id="search">
|
|
<form action="https://www.cliki.net/site/search">
|
|
<label for="search_query" class="hidden">Search CLiki</label>
|
|
<input type="text" name="query" id="search_query" value="" />
|
|
<input type="submit" value="search" />
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div id="pageheader">
|
|
<div id="header">
|
|
<span id="logo">CLiki</span>
|
|
<span id="slogan">the common lisp wiki</span>
|
|
<div id="login"><form method="post" action="https://www.cliki.net/site/login">
|
|
<label for="login_name" class="hidden">Account name</label>
|
|
<input type="text" name="name" id="login_name" class="login_input" />
|
|
<label for= "login_password" class="hidden">Password</label>
|
|
<input type="password" name="password" id="login_password" class="login_input" />
|
|
<input type="submit" name="login" value="login" id="login_submit" /><br />
|
|
<div id="register"><a href="https://www.cliki.net/site/register">register</a></div>
|
|
<input type="submit" name="reset-pw" value="reset password" id="reset_pw" />
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body></html> |