f5e7f6bd113f

temperance: Update site.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Sun, 22 Oct 2017 14:54:40 -0400
parents e79382c9ffcc
children 3895fb764d10
branches/tags (none)
files temperance/usage/index.html

Changes

--- 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">; =&gt;</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">; =&gt;</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">; =&gt;</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">; =&gt;</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">; =&gt;</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">; =&gt; 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">; =&gt; 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">; =&gt; 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">; =&gt; ((?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">; =&gt; ((?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">; =&gt; 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">; =&gt; 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">; =&gt; ((?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">; =&gt;</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">; =&gt;</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">; =&gt;</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>