{{ message }}
Repository navigation
Expand file tree
/
Copy pathoverview.html
More file actions
182 lines (97 loc) · 11.4 KB
/
Copy pathoverview.html
File metadata and controls
182 lines (97 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
<html>
<head>
<title>Overview of JavaPengines</title>
</head>
<body>
<h1>JavaPengines</h1>
<p>A Java language client for Torbjörn Lager's <em>Pengines</em> distributed computing library for
<em><a href="http://swi-prolog.org">SWI-Prolog</a></em> .</p>
<p>JavaPengines is a Java language implementation of the <em>Pengines</em> protocol, a lightweight, powerful query and update protocol for a <em>Pengines</em> server.</p>
<p>Pengines servers dramatically reduce the complexity of performing RPC and sharing <em>knowledge</em> in a mixed technology environment. They can act effectively as 'glue' in a complex system.</p>
<p>Currently there are clients for JavaScript (in browser), SWI-Prolog, and Java. We expect clients for Ruby, Python, and NodeJS to be written in the next few months.</p>
<p>The query language for Pengines is <em>Prolog</em>, and so unsurprisingly, the only current implementation of the Pengines server ships with the <em><a href="http://swi-prolog.org">SWI-Prolog</a></em> environment. SWI-Prolog is probably the most widely used implementation of Prolog, particularly suitable for large <em>real world</em> projects.</p>
<h2>What You need to know to use JavaPengines</h2>
<p>You do <em>not</em> need to be an expert in Prolog. The section below should teach you enough to make basic Pengines queries.</p>
<p>You do need to know Java, obviously.</p>
<p>You would also do well to familiarize yourself with the javax.json library.</p>
<h2>Installation</h2>
<p>JavaPengines is available as a jar from Maven Central with groupID com.simularity and ArtifactID com.simularity.os.javapengine, or as sources at <a href="http://www.github.com/Simularity/Javapengine/">Github</a>.</p>
<p>JavaPengines requires <em>javax.json</em>, which is available from Maven Central. </p>
<h2>Understanding JavaPengine</h2>
<p>JavaPengine is a thin wrapper around <a href="Pengines">http://pengines.swi-prolog.org/docs/index.html</a>, and so use requires knowledge of the Pengines system, although only minimal understanding of pengines or of Prolog is sufficient to make basic queries.</p>
<p>The Pengine architecture is simple. The client requests the server to create a Pengine <em>slave</em>. The client then sends one or more queries to the slave, and then tells the server to destroy the pengine.</p>
<p>For efficiency, a query can be sent along with the create query. The pengine can be told to destroy itself at the end of the first query. So a Pengine can be created, queried, and destroyed in as little as a single HTTP request.</p>
<p>The queries are simply Prolog code. So the entire power of the Prolog language is available to the client.</p>
<p>Obviously the Pengine server must <em>sandbox</em> the query. So some Prolog library predicates (e.g. shell) are unavailable. But, as much as is consistent with security, the standard Prolog libraries are available to the Pengine slave.</p>
<p>Additionally, Pengine servers usually expose some special predicates (Prolog 'functions' are called predicates). So, for example, a Prolog server could expose a predicate that allows a user to set their profile (presumably also passing some authentication).</p>
<p>Because the Pengine can last longer than one query, the client can store information on the server between queries. This can significantly reduce network traffic during complex graph queries.</p>
<h3>The Pengines Architecture</h3>
<p>Unlike imperative programs, in Prolog one constructs a "knowledgebase" of rules about a world, and then asks the system to find proofs of propositions, called queries. So there are two parts to a Prolog program - the rules, and the query.</p>
<p>Pengines extends SWI-Prolog to provide a distributed computing environment. Pengines exposes an HTTP based protocol that allows a remote system to submit queries and even alter the knowledgebase.</p>
<p>This is not unlike how web servers supply JavaScript to the browser to execute, with the client in role of the server and the pengine server in role of the web browser.</p>
<p>Each created pengine creates, effectively, it's own Prolog sub-VM. Different pengine slaves see different knowledgebases. </p>
<p>The pengine's knowledgebase is a combination of what the server chooses to expose, and what the client supplies. The server supplied part includes the safe parts of the standard Prolog libraries. </p>
<p>For a complete explanation of this process, watch <a href="my video from Strange Loop">https://www.youtube.com/watch?v=JmOHV5IlPyU</a></p>
<p>This provides lots of benefits. The remote client has a full, Turing complete language available for writing queries. Need some complex natural language scoring function computed to decide whether you need that row of data? Do it on the server.</p>
<p>The remote client can also store data on the server between invocations. Need to hang onto an intermediate stage query? Leave it on the server. Need to do graph traversal looking things? Do it on the server. Have a really complicated query you don't want to transmit for each query? Leave the code for it on the server.</p>
<h3>Life Cycle</h3>
<p>A slave pengine is created, used for zero or more queries, and then destroyed, either explicitly or by timing out.
A common, but not universal, pattern is to create a pengine, query it once, then destroy it. So JavaPengine supports this by allowing you to just make a query and repeatedly ask for the answers.</p>
<p>Making a Pengine for a single query is so common that it is the default for PengineBuilder. To retain the Pengine you must call setDestroy(false) on the PengineBuilder.</p>
<p>Prolog queries may return a single answer, but they can also fail (return no answer) or return many answers. This is fundamental to the SLD resolution mechanism at the core of Prolog.</p>
<p>In it's most basic use, the pengines protocol requires one HTTP request to create the pengine, one to initiate a query and get the first answer, one for each answer thereafter, and one to destroy the pengine.</p>
<p>But the pengines protocol allows the client to send the first query with the create message. This saves an HTTP round trip. The protocol also contains a flag that says 'destroy this pengine at the end of the first query'. This saves another round trip. For a pengine that is used for a single deterministic query, this reduces the number of HTTP requests from 3 to 1, a factor of 3 reduction in network traffic.</p>
<p>The getQuery and getProof methods use these optimizations internally. Their use when appropriate can reduce network traffic.</p>
<h3>Slave Limit</h3>
<p>Pengine servers have a per-client slave limit. Clients must destroy pengines before making new ones to stay under the limit.</p>
<h2>## API</h2>
<p>Making a Pengine starts with <code>com.simularity.os.javapengine.PengineBuilder</code>. </p>
<p>Make a new PengineBuilder object, set some values on it, and then user it to make one or more Pengines.</p>
<p>the <code>setDestroy(boolean)</code> method is particularly important to set. By default it's true, and when the first query done on the pengine has returned it's last result, the pengine destroys itself.</p>
<p>Some arguments to create a Pengine change with each Pengine, like ask. Some are usually constant, like the server's name. It can be useful to have a prototype PengineBuilder around and clone it, then change values on the clone before making the Pengine.</p>
<p>Call <code>newPengine()</code> to get a new Pengine.</p>
<p>If you supplied an ask to the PengineBuilder via <code>setAsk(String)</code>, the Pengine will already be executing a query. You can get the query via <code>getCurrentQuery()</code>.</p>
<p>If not, you'll need to start a query with <code>ask(String)</code>.</p>
<p>Both of these return a <code>com.simularity.os.javapengine.Query</code> object.</p>
<p>if the <code>hasNext()</code> method returns true, the query <em>may</em> have more answers. <code>next()</code> returns the next Proof, or null if there are none.</p>
<p>The Proof object has a key-value map that maps variables in the query to values. So </p>
<hr />
<h2> member(X, [a,b,c])</h2>
<p>would result in <code>{ "X": "a"}</code> as the first proof, <code>{ "X": "b"}</code> as the second, and <code>{ "X": "c"}</code> as the last.</p>
<p>There are convenience methods for extracting common Java types from the JSON structure.</p>
<p>If you want to stop getting solutions before they're exhausted, Query has a <code>stop()</code> method.</p>
<p>After you have stopped or exhausted the solutions, you can start another query. Each Pengine can be used for only one query at a time.</p>
<p>When you are done with the Pengine, call destroy() on it. This will happen automatically if you left setDestroy set to true.</p>
<h2>Don't Know Prolog</h2>
<p>If you don't know Prolog, you can do most basic queries with this introduction.</p>
<h3>Atoms and Variables</h3>
<p>Prolog is case sensitive. Variables start with an uppercase letter, so ThisIsAVariable. All other identifiers are atoms, which either start
with a lowercase letter, and consist of letters, numbers, and underscore: this<em>is</em>an_atom, or are enclosed in single quotes: '!Wow, also an atom!'. +'taxes'+ and +taxes+ are the same atom - the single quotes are optional here.</p>
<h3>Queries</h3>
<p>Most pengines queries are simply a call to a single predicate (sort of like a function in Prolog). </p>
<hr />
<h2> employee_info('Bob Smith', Position, Salary)</h2>
<p>The results will <em>bind</em> Position and Salary to whatever's appropriate for Bob. This means we can ask strange questions like
who has a salary of 85000.</p>
<hr />
<h2> employee_info(Name, Position, 85000)</h2>
<p>It's up to the implementer on the Prolog side which of of these 'modes' are actually implemented. You'll need to check with the documentation of the Prolog code. A commonly used convention in the Prolog community is to write a + meaning the argument must be supplied, a - to mean the predicate will fill it in, and ? to mean either is acceptable. So, we can probably ask for any combination of arguments for employee<em>info. This would be documented as employee</em>info(?, ?, ?).</p>
<h3>Underscore</h3>
<p>We really didn't ask for the position, we just wanted the name. So we can put an underscore in the second argument to say we're not interested in it.</p>
<hr />
<h2> employee_info(Name, _, 85000)</h2>
<h3>Strings</h3>
<p>Notice that Bob's name is an atom. Prolog also has "real strings", and "codes strings". All get converted to Java Strings.</p>
<h3>Nondeterminism</h3>
<p>Now, if we happen to have two Bob Smiths in the company? We'll get two rows of data. If there is no Bob Smith, we'll get no data. So Queries always return an iterator.</p>
<p>One tricky bit about this. Prolog returns all the ways it can 'prove' the employee info is true. This sometimes means it will return multiple copies of a single answer. If you need rows to be distinct, work with the Prolog programmer.</p>
<h3>Limiting answers</h3>
<p>I said above that most queries are a single predicate. An exception to that is that you might want to limit your query to a range. Here's how to do it.</p>
<hr />
<h2> (employee_info(Name, _, Salary), Salary > 85000, Salary =< 120000)</h2>
<p>The commas mean 'and' in this context. You can use another predicate as well. Say you have +department(Name, Department)+ and want to find salaries of employees in marketing.</p>
<hr />
<h2> (employee_info(Name, _, Salary), department(Name, marketing))</h2>
<p>Notice, as an aside, that marketing is an atom. </p>
</body>
</html>
You can’t perform that action at this time.
