# HG changeset patch # User Steve Losh <steve@stevelosh.com> # Date 1508698480 14400 # Node ID f5e7f6bd113fa8d8edafbcf4795b2c06dee56144 # Parent e79382c9ffcca8445880a50729cef223faa39838 temperance: Update site. diff -r e79382c9ffcc -r f5e7f6bd113f temperance/usage/index.html --- a/temperance/usage/index.html Tue Apr 25 15:14:51 2017 +0000 +++ b/temperance/usage/index.html Sun Oct 22 14:54:40 2017 -0400 @@ -12,28 +12,332 @@ <div class="wrap"> <header><h1><a href="..">Temperance</a></h1></header> <div class="markdown"> -<h1 id="usage"><a href="">Usage</a></h1><div class="toc"> +<h1 id="usage"><a href="">Usage</a></h1><p>This guide assumes you know the basics of logic programming. If you've never +done any logic programming before, you should probably start with an +introduction to that before trying to read this.</p> +<div class="toc"> <ul> <li><a href="#hello-temperance">Hello, Temperance</a></li> <li><a href="#databases">Databases</a></li> -<li><a href="#rules">Rules</a></li> +<li><a href="#logic-frames">Logic Frames</a><ul> +<li><a href="#assertion-pushing">Assertion / Pushing</a></li> +<li><a href="#retraction-popping">Retraction / Popping</a></li> +<li><a href="#convenience">Convenience</a></li> +<li><a href="#restrictions">Restrictions</a></li> +<li><a href="#why">Why?</a></li> +</ul> +</li> +<li><a href="#rules">Rules</a><ul> +<li><a href="#basic-rulefact-macros">Basic Rule/Fact Macros</a></li> +<li><a href="#dynamic-rules">Dynamic Rules</a></li> +</ul> +</li> +<li><a href="#queries">Queries</a><ul> +<li><a href="#query">query</a></li> +<li><a href="#query-all">query-all</a></li> +<li><a href="#query-map">query-map</a></li> +<li><a href="#query-for">query-for</a></li> +<li><a href="#query-do">query-do</a></li> +<li><a href="#query-find">query-find</a></li> +<li><a href="#prove">prove</a></li> <li><a href="#anonymous-variables">Anonymous Variables</a></li> -<li><a href="#queries">Queries</a></li> +<li><a href="#dynamic-queries">Dynamic Queries</a></li> +</ul> +</li> <li><a href="#lists">Lists</a></li> -<li><a href="#logic-frames">Logic Frames</a></li> <li><a href="#cut">Cut</a></li> -<li><a href="#dynamic-queries-and-rules">Dynamic Queries and Rules</a></li> +<li><a href="#call">Call</a></li> <li><a href="#built-in-predicates">Built-In Predicates</a></li> </ul></div> <h2 id="hello-temperance">Hello, Temperance</h2> +<p>Temperance is a pretty big system and we'll need to look at a few different +pieces one by one before you'll have a good picture of how it all fits +together, but let's at least get something on the screen. First we'll set up +a logic database with some basic facts:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">in-package</span> <span class="ss">:cl-user</span><span class="p">)</span> +<span class="p">(</span><span class="nb">use-package</span> <span class="ss">:temperance</span><span class="p">)</span> + +<span class="p">(</span><span class="nv">push-logic-frame</span> <span class="no">t</span><span class="p">)</span> + +<span class="p">(</span><span class="nv">facts</span> <span class="no">t</span> + <span class="p">(</span><span class="nv">likes</span> <span class="nv">steve</span> <span class="nv">cats</span><span class="p">)</span> + <span class="p">(</span><span class="nv">likes</span> <span class="nv">steve</span> <span class="nv">beer</span><span class="p">)</span> + <span class="p">(</span><span class="nv">likes</span> <span class="nv">kim</span> <span class="nv">cats</span><span class="p">)</span> + <span class="p">(</span><span class="nv">likes</span> <span class="nv">kim</span> <span class="nv">tea</span><span class="p">)</span> + <span class="p">(</span><span class="nv">likes</span> <span class="nv">sally</span> <span class="nv">cats</span><span class="p">)</span> + <span class="p">(</span><span class="nv">likes</span> <span class="nv">sally</span> <span class="nv">beer</span><span class="p">))</span> + +<span class="p">(</span><span class="nv">rule</span> <span class="no">t</span> <span class="p">(</span><span class="nv">likes</span> <span class="nv">sally</span> <span class="nv">?who</span><span class="p">)</span> + <span class="p">(</span><span class="nv">likes</span> <span class="nv">?who</span> <span class="nv">cats</span><span class="p">))</span> + +<span class="p">(</span><span class="nv">finalize-logic-frame</span> <span class="no">t</span><span class="p">)</span> +</pre></div> + + +<p>Ignore the stuff about logic frames for now. We've added some facts saying:</p> +<ul> +<li>Steve and Sally like cats and beer</li> +<li>Kim likes cats and tea</li> +<li>Sally also likes anybody who likes cats</li> +</ul> +<p>And now we can ask some questions:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">query-all</span> <span class="no">t</span> <span class="p">(</span><span class="nv">likes</span> <span class="nv">?who</span> <span class="nv">beer</span><span class="p">))</span> +<span class="c1">; =></span> +<span class="c1">; ((?WHO STEVE)</span> +<span class="c1">; (?WHO SALLY))</span> + +<span class="p">(</span><span class="nv">query-all</span> <span class="no">t</span> <span class="p">(</span><span class="nv">likes</span> <span class="nv">sally</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; =></span> +<span class="c1">; ((?WHAT CATS)</span> +<span class="c1">; (?WHAT BEER)</span> +<span class="c1">; (?WHAT STEVE)</span> +<span class="c1">; (?WHAT KIM)</span> +<span class="c1">; (?WHAT SALLY))</span> + +<span class="p">(</span><span class="nv">query-all</span> <span class="no">t</span> <span class="p">(</span><span class="nv">likes</span> <span class="nv">?who</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; =></span> +<span class="c1">; ((?WHAT CATS ?WHO STEVE)</span> +<span class="c1">; (?WHAT BEER ?WHO STEVE)</span> +<span class="c1">; (?WHAT CATS ?WHO KIM)</span> +<span class="c1">; (?WHAT TEA ?WHO KIM)</span> +<span class="c1">; (?WHAT CATS ?WHO SALLY)</span> +<span class="c1">; (?WHAT BEER ?WHO SALLY)</span> +<span class="c1">; (?WHAT STEVE ?WHO SALLY)</span> +<span class="c1">; (?WHAT KIM ?WHO SALLY)</span> +<span class="c1">; (?WHAT SALLY ?WHO SALLY))</span> +</pre></div> + + <h2 id="databases">Databases</h2> +<p>The main data structure of Temperance is a database. Most functions in +Temperance's API take this as their first argument.</p> +<p>You can create a database with <code>make-database</code>:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">make-database</span><span class="p">))</span> +<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*db2*</span> <span class="p">(</span><span class="nv">make-database</span><span class="p">))</span> +</pre></div> + + +<p>You can then use it in other functions:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">push-logic-frame</span> <span class="vg">*db*</span><span class="p">)</span> + +<span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">coffee</span><span class="p">))</span> +<span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">tea</span><span class="p">))</span> +<span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">water</span><span class="p">))</span> + +<span class="p">(</span><span class="nv">finalize-logic-frame</span> <span class="vg">*db*</span><span class="p">)</span> + +<span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; =></span> +<span class="c1">; ((?WHAT COFFEE)</span> +<span class="c1">; (?WHAT TEA)</span> +<span class="c1">; (?WHAT WATER))</span> + +<span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db2*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; =></span> +<span class="c1">; NIL</span> +</pre></div> + + +<p>Temperance also creates a database by default and stores it in +<code>*standard-database*</code>. You can pass <code>t</code> to functions instead of a database +object to operate on this standard database if you don't want to bother creating +one of your own.</p> +<p>Databases are not (currently) thread-safe. The consequences are undefined if +you access the same database object concurrently. Thread-safety is on the +<strong>TODO</strong> list.</p> +<h2 id="logic-frames">Logic Frames</h2> +<p>Temperance supports adding/retracting rules and facts to/from a database at +runtime, but it has a special interface for doing this in the interest of speed.</p> +<p>(Note: from here on out we'll say "rules" to mean "rules and facts", because +facts are just rules with empty bodies.)</p> +<h3 id="assertion-pushing">Assertion / Pushing</h3> +<p>Each Temperance database has a "logic stack" consisting of zero or more "logic +frames". To add some rules to the database you push a logic frame onto the +stack, add the rules, and then finalize the logic frame to compile the rules +into Prolog bytecode:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">make-database</span><span class="p">))</span> + +<span class="c1">; Nothing's in the database yet.</span> +<span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; => NIL</span> + +<span class="c1">; Push a logic frame.</span> +<span class="p">(</span><span class="nv">push-logic-frame</span> <span class="vg">*db*</span><span class="p">)</span> + +<span class="c1">; Still nothing in the DB.</span> +<span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; => NIL</span> + +<span class="c1">; Add some facts.</span> +<span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">coffee</span><span class="p">))</span> +<span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">tea</span><span class="p">))</span> +<span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">water</span><span class="p">))</span> + +<span class="c1">; There's STILL nothing in the DB, because we haven't</span> +<span class="c1">; finalized the frame yet!</span> +<span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; => NIL</span> + +<span class="c1">; Finalize the frame. This compiles `drink`'s rules</span> +<span class="c1">; into bytecode.</span> +<span class="p">(</span><span class="nv">finalize-logic-frame</span> <span class="vg">*db*</span><span class="p">)</span> + +<span class="c1">; And now we can finally see some results.</span> +<span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; => ((?WHAT COFFEE) (?WHAT TEA) (?WHAT WATER))</span> +</pre></div> + + +<h3 id="retraction-popping">Retraction / Popping</h3> +<p>You can pop a logic frame off the stack to retract everything inside it:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; => ((?WHAT COFFEE) (?WHAT TEA) (?WHAT WATER))</span> + +<span class="p">(</span><span class="nv">pop-logic-frame</span> <span class="vg">*db*</span><span class="p">)</span> + +<span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">drink</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; => NIL</span> +</pre></div> + + +<h3 id="convenience">Convenience</h3> +<p>Most of the time you'll want to push a logic frame, add some stuff, and then +immediately finalize it. Temperance provides a <code>push-logic-frame-with</code> macro to +make this easier:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">push-logic-frame-with</span> <span class="vg">*db*</span> + <span class="p">(</span><span class="nb">princ</span> <span class="s">"Adding some facts..."</span><span class="p">)</span> + <span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">sound</span> <span class="nv">cat</span> <span class="nv">meow</span><span class="p">))</span> + <span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">sound</span> <span class="nv">dog</span> <span class="nv">woof</span><span class="p">))</span> + <span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">sound</span> <span class="nv">cow</span> <span class="nv">moo</span><span class="p">)))</span> +<span class="c1">; => Adding some facts...</span> + +<span class="p">(</span><span class="nv">query-all</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">sound</span> <span class="nv">cat</span> <span class="nv">?what</span><span class="p">))</span> +<span class="c1">; => ((?WHAT MEOW))</span> +</pre></div> + + +<p>Note how the body of <code>push-logic-frame-with</code> takes arbitrary code, not just +rules and facts.</p> +<h3 id="restrictions">Restrictions</h3> +<p>There main limitation introduced by Temperance's logic stack is that any +particular predicate must exist entirely in a single logic frame. You <em>cannot</em> +spread the definition of a single predicate across multiple frames:</p> +<div class="codehilite"><pre><span/><span class="c1">; This is fine</span> +<span class="p">(</span><span class="nv">push-logic-frame-with</span> <span class="vg">*db*</span> + <span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">sound</span> <span class="nv">cat</span> <span class="nv">meow</span><span class="p">)))</span> + +<span class="c1">; ERROR! `sound/2` was already defined in a previous frame!</span> +<span class="p">(</span><span class="nv">push-logic-frame-with</span> <span class="vg">*db*</span> + <span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">sound</span> <span class="nv">dog</span> <span class="nv">woof</span><span class="p">)))</span> +</pre></div> + + +<h3 id="why">Why?</h3> +<p>Why does Temperance bother with this stack interface? Why not just allow +arbitrary assertion and retraction of facts?</p> +<p>Arbitrary assertion and retraction would be a friendlier interface, but would be +slower. Temperance was made with General Game Playing in mind, where speed is +important. Asserting and retracting facts with a stack like this allows several +speed improvements:</p> +<ul> +<li>Predicates only need to be compiled once (when their frame is finalized), + instead of being recompiled every time a new clause is asserted.</li> +<li>Retraction of a frame is lighting fast — it's just changing a single integer + and zeroing out a contiguous block of memory.</li> +<li>The VM's code store no longer needs to deal with memory fragmentation or + garbage collection.</li> +</ul> <h2 id="rules">Rules</h2> -<h2 id="anonymous-variables">Anonymous Variables</h2> +<p>Once you've got an open logic frame you can add some rules to the database.</p> +<h3 id="basic-rulefact-macros">Basic Rule/Fact Macros</h3> +<p>Temperance offers a number of macros for adding things to a logic frame. <code>(fact +database fact)</code> is the simplest, and will just add a single fact to the +database:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">fact</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">cat</span> <span class="nv">scruffy</span><span class="p">))</span> +</pre></div> + + +<p>If you want to add more than one fact you can use <code>(facts database fact...)</code>:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">facts</span> <span class="vg">*db*</span> + <span class="p">(</span><span class="nv">cat</span> <span class="nv">fluffy</span><span class="p">)</span> + <span class="p">(</span><span class="nv">dog</span> <span class="nv">rover</span><span class="p">)</span> + <span class="p">(</span><span class="nv">dog</span> <span class="nv">lassie</span><span class="p">)</span> + <span class="p">(</span><span class="nv">dog</span> <span class="nv">spot</span><span class="p">))</span> +</pre></div> + + +<p>Adding rules can be done with <code>(rule database head body...)</code>:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">rule</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">pet</span> <span class="nv">?who</span><span class="p">)</span> + <span class="p">(</span><span class="nv">cat</span> <span class="nv">?who</span><span class="p">))</span> + +<span class="p">(</span><span class="nv">rule</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">pet</span> <span class="nv">?who</span><span class="p">)</span> + <span class="p">(</span><span class="nv">dog</span> <span class="nv">?who</span><span class="p">))</span> +</pre></div> + + +<p>Logic variables in Temperance are any symbols that start with the <code>?</code> character.</p> +<h3 id="dynamic-rules">Dynamic Rules</h3> +<p>The <code>fact</code>, <code>facts</code>, and <code>rule</code> macros quote their arguments. If you need to +build rules at runtime you'll need to use the <code>invoke-...</code> variants and manage +the quoting yourself:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defun</span> <span class="nv">add-cat-lover</span> <span class="p">(</span><span class="nv">database</span> <span class="nv">person</span><span class="p">)</span> + <span class="p">(</span><span class="nv">invoke-rule</span> <span class="nv">database</span> + <span class="o">`</span><span class="p">(</span><span class="nv">likes</span> <span class="o">,</span><span class="nv">person</span> <span class="nv">?who</span><span class="p">)</span> + <span class="o">'</span><span class="p">(</span><span class="nv">cat</span> <span class="nv">?who</span><span class="p">)))</span> +</pre></div> + + +<p>The author is aware that <code>invoke-...</code> is an awful name and welcomes suggestions +for something better.</p> <h2 id="queries">Queries</h2> +<p>Once you've got some logic frames finalized and ready to go you can query the +database with the <code>query...</code> macros.</p> +<h3 id="query">query</h3> +<p><code>(query database body...)</code> will return two values:</p> +<ul> +<li>A plist of the substitutions necessary to make <code>body</code> true (or <code>nil</code> if none + is possible).</li> +<li><code>t</code> if the query was successful, <code>nil</code> otherwise.</li> +</ul> +<p>The second value lets you distinguish a successful query that required no +bindings from an unsuccessful query. For example:</p> +<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">make-database</span><span class="p">))</span> + +<span class="p">(</span><span class="nv">push-logic-frame-with</span> <span class="vg">*db*</span> + <span class="p">(</span><span class="nv">facts</span> <span class="vg">*db*</span> + <span class="p">(</span><span class="nv">cat</span> <span class="nv">scruffy</span><span class="p">)</span> + <span class="p">(</span><span class="nv">cat</span> <span class="nv">fluffy</span><span class="p">)))</span> + +<span class="p">(</span><span class="nv">query</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">cat</span> <span class="nv">fluffy</span><span class="p">))</span> +<span class="c1">; =></span> +<span class="c1">; NIL</span> +<span class="c1">; T successful, no bindings needed</span> + +<span class="p">(</span><span class="nv">query</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">cat</span> <span class="nv">chad</span><span class="p">))</span> +<span class="c1">; =></span> +<span class="c1">; NIL</span> +<span class="c1">; NIL unsuccessful</span> + +<span class="p">(</span><span class="nv">query</span> <span class="vg">*db*</span> <span class="p">(</span><span class="nv">cat</span> <span class="nv">?who</span><span class="p">))</span> +<span class="c1">; =></span> +<span class="c1">; (?WHO SCRUFFY)</span> +<span class="c1">; T</span> +</pre></div> + + +<p><code>query</code> only returns the first set of substitutions it finds. If you want to see <em>all</em> results you'll need to use <code>query-all</code>.</p> +<h3 id="query-all">query-all</h3> +<h3 id="query-map">query-map</h3> +<h3 id="query-for">query-for</h3> +<h3 id="query-do">query-do</h3> +<h3 id="query-find">query-find</h3> +<h3 id="prove">prove</h3> +<h3 id="anonymous-variables">Anonymous Variables</h3> +<p>Each bare <code>?</code> symbol is treated as a separate anonymous variable:</p> +<h3 id="dynamic-queries">Dynamic Queries</h3> <h2 id="lists">Lists</h2> -<h2 id="logic-frames">Logic Frames</h2> <h2 id="cut">Cut</h2> -<h2 id="dynamic-queries-and-rules">Dynamic Queries and Rules</h2> +<h2 id="call">Call</h2> <h2 id="built-in-predicates">Built-In Predicates</h2> </div> <footer><p><i>Made with Lisp and love by <a href="http://stevelosh.com/">Steve Losh</a> in Reykjavík, Iceland.</i></p>