on master help sign in
Merge branch '168_jupyter_kernel'
commitdcaf650bda71ff7179e537c1121d3ec30328824c authorAgentM <agentm@themactionfaction.com> dateWed 15 Nov 2017, 23:47 (9 years ago) parent967c6535c26dba
9 files changed+926−51
.gitignore+5−1 README.markdown+1 docs/jupyter_kernel.markdown+1−1 jupyter/TutorialD Notebook Walkthrough.ipynb+794 jupyter/itutd/ITutorialDKernel.py+104−39 jupyter/setup.py+1−1 src/lib/ProjectM36/MiscUtils.hs+6 src/lib/ProjectM36/Relation/Show/HTML.hs+6−5 src/lib/ProjectM36/Tuple.hs+8−4
.gitignore+5−1
@@ -14,4 +14,8 @@ nextsteps
1414\#*\#
1515view-system-notes
1616persistence-notes
17−*.pyc
17+*.pyc
18+jupyter/itutd.egg-info
19+jupyter/build
20+juyter/dist
21+.ipynb_checkpoints
README.markdown+1
@@ -50,6 +50,7 @@ Project:M36 supports multiple frontends which target different audiences.
5050
51511. [Installation and Introduction to Project:M36](docs/introduction_to_projectm36.markdown)
52521. [Introduction to the Relational Algebra](docs/introduction_to_the_relational_algebra.markdown)
53+1. [TutorialD via Jupyter Notebook Walkthrough](jupyter/TutorialD%20Notebook%20Walkthrough.ipynb)
53541. [TutorialD Tutorial](docs/tutd_tutorial.markdown)
54551. [15 Minute Tutorial](docs/15_minute_tutorial.markdown)
55561. [Developer's Change Log](Changelog.markdown)
docs/jupyter_kernel.markdown+1−1
@@ -13,7 +13,7 @@ To install the `tutd` kernel:
1313* install the `itutd` module into your jupyter virtual environment:
1414```
1515$ cd project-m36/jupyter/itutd
16−$ python setup.py install
16+$ pip install .
1717```
1818
1919* install `kernel.json`:
jupyter/TutorialD Notebook Walkthrough.ipynbnew file+794
@@ -0,0 +1,794 @@
1+{
2+ "cells": [
3+ {
4+ "cell_type": "markdown",
5+ "metadata": {},
6+ "source": [
7+ "# Introduction to TutorialD via Jupyter Notebook\n",
8+ "\n",
9+ "Thanks for trying Project:M36 via this Jupyter notebook. TutorialD is a language for interacting with relational algebra databases, similar to SQL but without the historical cruft. This notebook is based on the [TutorialD Tutorial](https://github.com/agentm/project-m36/blob/master/docs/tutd_tutorial.markdown). \n",
10+ "\n",
11+ "[Project:M36](https://github.com/agentm/project-m36) is a feature-rich database management system (DBMS) which casts off the baggage of legacy DBMSes by adhering strictly to the mathematics of the relational algebra.\n",
12+ "\n",
13+ "Please refer to the [Project:M36 documentation](https://github.com/agentm/project-m36#documentation) for more detailed examples. Let's get started!\n",
14+ "\n",
15+ "First, let's import the Chris Date example relation variables. The relation variables provided in the example are:\n",
16+ "\n",
17+ "* `s` suppliers\n",
18+ "* `sp` supplier-parts\n",
19+ "* `p` parts"
20+ ]
21+ },
22+ {
23+ "cell_type": "code",
24+ "execution_count": 2,
25+ "metadata": {},
26+ "outputs": [
27+ {
28+ "data": {
29+ "text/html": [
30+ "ok"
31+ ],
32+ "text/plain": [
33+ "ok"
34+ ]
35+ },
36+ "metadata": {},
37+ "output_type": "display_data"
38+ }
39+ ],
40+ "source": [
41+ ":importexample date"
42+ ]
43+ },
44+ {
45+ "cell_type": "markdown",
46+ "metadata": {},
47+ "source": [
48+ "If you see an error in the previous cell regarding `project-m36-websocket-server` not being in your `PATH`, add the binary to your `PATH` and restart the jupyter server.\n",
49+ "\n",
50+ "Let's take a look at the relation variables."
51+ ]
52+ },
53+ {
54+ "cell_type": "code",
55+ "execution_count": 3,
56+ "metadata": {},
57+ "outputs": [
58+ {
59+ "data": {
60+ "text/html": [
61+ "<table border=\"1\"><tr><th>s#::Text</th><th>sname::Text</th><th>status::Integer</th><th>city::Text</th></tr><tr><td>\"S3\"</td><td>\"Blake\"</td><td>30</td><td>\"Paris\"</td></tr><tr><td>\"S5\"</td><td>\"Adams\"</td><td>30</td><td>\"Athens\"</td></tr><tr><td>\"S4\"</td><td>\"Clark\"</td><td>20</td><td>\"London\"</td></tr><tr><td>\"S1\"</td><td>\"Smith\"</td><td>20</td><td>\"London\"</td></tr><tr><td>\"S2\"</td><td>\"Jones\"</td><td>10</td><td>\"Paris\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 5 tuples</td></tr></tfoot></table>"
62+ ],
63+ "text/plain": [
64+ "┌──────────┬────────┬───────────┬───────────────┐\n",
65+ "│city::Text│s#::Text│sname::Text│status::Integer│\n",
66+ "├──────────┼────────┼───────────┼───────────────┤\n",
67+ "│\"Paris\" │\"S2\" │\"Jones\" │10 │\n",
68+ "│\"London\" │\"S1\" │\"Smith\" │20 │\n",
69+ "│\"London\" │\"S4\" │\"Clark\" │20 │\n",
70+ "│\"Athens\" │\"S5\" │\"Adams\" │30 │\n",
71+ "│\"Paris\" │\"S3\" │\"Blake\" │30 │\n",
72+ "└──────────┴────────┴───────────┴───────────────┘"
73+ ]
74+ },
75+ "metadata": {},
76+ "output_type": "display_data"
77+ }
78+ ],
79+ "source": [
80+ ":showexpr s"
81+ ]
82+ },
83+ {
84+ "cell_type": "code",
85+ "execution_count": 4,
86+ "metadata": {},
87+ "outputs": [
88+ {
89+ "data": {
90+ "text/html": [
91+ "<table border=\"1\"><tr><th>s#::Text</th><th>p#::Text</th><th>qty::Integer</th></tr><tr><td>\"S1\"</td><td>\"P2\"</td><td>200</td></tr><tr><td>\"S1\"</td><td>\"P1\"</td><td>300</td></tr><tr><td>\"S3\"</td><td>\"P2\"</td><td>200</td></tr><tr><td>\"S2\"</td><td>\"P2\"</td><td>400</td></tr><tr><td>\"S4\"</td><td>\"P5\"</td><td>400</td></tr><tr><td>\"S1\"</td><td>\"P5\"</td><td>100</td></tr><tr><td>\"S1\"</td><td>\"P6\"</td><td>100</td></tr><tr><td>\"S2\"</td><td>\"P1\"</td><td>300</td></tr><tr><td>\"S1\"</td><td>\"P4\"</td><td>200</td></tr><tr><td>\"S4\"</td><td>\"P4\"</td><td>300</td></tr><tr><td>\"S4\"</td><td>\"P2\"</td><td>200</td></tr><tr><td>\"S1\"</td><td>\"P3\"</td><td>400</td></tr><tfoot><tr><td colspan=\"100%\">Finite 12 tuples</td></tr></tfoot></table>"
92+ ],
93+ "text/plain": [
94+ "┌────────┬────────────┬────────┐\n",
95+ "│p#::Text│qty::Integer│s#::Text│\n",
96+ "├────────┼────────────┼────────┤\n",
97+ "│\"P3\" │400 │\"S1\" │\n",
98+ "│\"P2\" │200 │\"S4\" │\n",
99+ "│\"P4\" │300 │\"S4\" │\n",
100+ "│\"P4\" │200 │\"S1\" │\n",
101+ "│\"P1\" │300 │\"S2\" │\n",
102+ "│\"P6\" │100 │\"S1\" │\n",
103+ "│\"P5\" │100 │\"S1\" │\n",
104+ "│\"P5\" │400 │\"S4\" │\n",
105+ "│\"P2\" │400 │\"S2\" │\n",
106+ "│\"P2\" │200 │\"S3\" │\n",
107+ "│\"P1\" │300 │\"S1\" │\n",
108+ "│\"P2\" │200 │\"S1\" │\n",
109+ "└────────┴────────────┴────────┘"
110+ ]
111+ },
112+ "metadata": {},
113+ "output_type": "display_data"
114+ }
115+ ],
116+ "source": [
117+ ":showexpr sp"
118+ ]
119+ },
120+ {
121+ "cell_type": "code",
122+ "execution_count": 5,
123+ "metadata": {
124+ "scrolled": true
125+ },
126+ "outputs": [
127+ {
128+ "data": {
129+ "text/html": [
130+ "<table border=\"1\"><tr><th>p#::Text</th><th>pname::Text</th><th>color::Text</th><th>weight::Integer</th><th>city::Text</th></tr><tr><td>\"P3\"</td><td>\"Screw\"</td><td>\"Blue\"</td><td>17</td><td>\"Oslo\"</td></tr><tr><td>\"P2\"</td><td>\"Bolt\"</td><td>\"Green\"</td><td>17</td><td>\"Paris\"</td></tr><tr><td>\"P4\"</td><td>\"Screw\"</td><td>\"Red\"</td><td>14</td><td>\"London\"</td></tr><tr><td>\"P6\"</td><td>\"Cog\"</td><td>\"Red\"</td><td>19</td><td>\"London\"</td></tr><tr><td>\"P1\"</td><td>\"Nut\"</td><td>\"Red\"</td><td>12</td><td>\"London\"</td></tr><tr><td>\"P5\"</td><td>\"Cam\"</td><td>\"Blue\"</td><td>12</td><td>\"Paris\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 6 tuples</td></tr></tfoot></table>"
131+ ],
132+ "text/plain": [
133+ "┌──────────┬───────────┬────────┬───────────┬───────────────┐\n",
134+ "│city::Text│color::Text│p#::Text│pname::Text│weight::Integer│\n",
135+ "├──────────┼───────────┼────────┼───────────┼───────────────┤\n",
136+ "│\"Paris\" │\"Blue\" │\"P5\" │\"Cam\" │12 │\n",
137+ "│\"London\" │\"Red\" │\"P1\" │\"Nut\" │12 │\n",
138+ "│\"London\" │\"Red\" │\"P6\" │\"Cog\" │19 │\n",
139+ "│\"London\" │\"Red\" │\"P4\" │\"Screw\" │14 │\n",
140+ "│\"Paris\" │\"Green\" │\"P2\" │\"Bolt\" │17 │\n",
141+ "│\"Oslo\" │\"Blue\" │\"P3\" │\"Screw\" │17 │\n",
142+ "└──────────┴───────────┴────────┴───────────┴───────────────┘"
143+ ]
144+ },
145+ "metadata": {},
146+ "output_type": "display_data"
147+ }
148+ ],
149+ "source": [
150+ "\n",
151+ ":showexpr p"
152+ ]
153+ },
154+ {
155+ "cell_type": "markdown",
156+ "metadata": {},
157+ "source": [
158+ "## Relational Operators\n",
159+ "\n",
160+ "### Restriction\n",
161+ "\n",
162+ "We can see that there is a many-to-many relationship between `s` (suppliers) and `p` (parts) via the `sp` relation variable.\n",
163+ "\n",
164+ "Let's look at the suppliers who are in London. This is called \"restriction\".\n",
165+ "\n",
166+ "Note that the relation attributes have appropriate types which restrict the values which they can contain."
167+ ]
168+ },
169+ {
170+ "cell_type": "code",
171+ "execution_count": 9,
172+ "metadata": {},
173+ "outputs": [
174+ {
175+ "data": {
176+ "text/html": [
177+ "<table border=\"1\"><tr><th>s#::Text</th><th>sname::Text</th><th>status::Integer</th><th>city::Text</th></tr><tr><td>\"S4\"</td><td>\"Clark\"</td><td>20</td><td>\"London\"</td></tr><tr><td>\"S1\"</td><td>\"Smith\"</td><td>20</td><td>\"London\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 2 tuples</td></tr></tfoot></table>"
178+ ],
179+ "text/plain": [
180+ "┌──────────┬────────┬───────────┬───────────────┐\n",
181+ "│city::Text│s#::Text│sname::Text│status::Integer│\n",
182+ "├──────────┼────────┼───────────┼───────────────┤\n",
183+ "│\"London\" │\"S1\" │\"Smith\" │20 │\n",
184+ "│\"London\" │\"S4\" │\"Clark\" │20 │\n",
185+ "└──────────┴────────┴───────────┴───────────────┘"
186+ ]
187+ },
188+ "metadata": {},
189+ "output_type": "display_data"
190+ }
191+ ],
192+ "source": [
193+ ":showexpr s where city=\"London\""
194+ ]
195+ },
196+ {
197+ "cell_type": "markdown",
198+ "metadata": {},
199+ "source": [
200+ "### Join And Projection\n",
201+ "\n",
202+ "Let's examine which parts the London suppliers offer. In the following expression, we make use of the relational join operator (equivalent to `NATURAL JOIN` in SQL) and projection using the trailing curly brackets to only return the relation attributes which interest us."
203+ ]
204+ },
205+ {
206+ "cell_type": "code",
207+ "execution_count": 11,
208+ "metadata": {},
209+ "outputs": [
210+ {
211+ "data": {
212+ "text/html": [
213+ "<table border=\"1\"><tr><th>qty::Integer</th><th>pname::Text</th><th>p#::Text</th></tr><tr><td>100</td><td>\"Cog\"</td><td>\"P6\"</td></tr><tr><td>300</td><td>\"Nut\"</td><td>\"P1\"</td></tr><tr><td>300</td><td>\"Screw\"</td><td>\"P4\"</td></tr><tr><td>200</td><td>\"Screw\"</td><td>\"P4\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 4 tuples</td></tr></tfoot></table>"
214+ ],
215+ "text/plain": [
216+ "┌────────┬───────────┬────────────┐\n",
217+ "│p#::Text│pname::Text│qty::Integer│\n",
218+ "├────────┼───────────┼────────────┤\n",
219+ "│\"P4\" │\"Screw\" │200 │\n",
220+ "│\"P4\" │\"Screw\" │300 │\n",
221+ "│\"P1\" │\"Nut\" │300 │\n",
222+ "│\"P6\" │\"Cog\" │100 │\n",
223+ "└────────┴───────────┴────────────┘"
224+ ]
225+ },
226+ "metadata": {},
227+ "output_type": "display_data"
228+ }
229+ ],
230+ "source": [
231+ ":showexpr ((s where city=\"London\") join sp join p){p#,pname,qty}"
232+ ]
233+ },
234+ {
235+ "cell_type": "markdown",
236+ "metadata": {},
237+ "source": [
238+ "### Extend and Atom Functions\n",
239+ "\n",
240+ "We can also execute server-side functions on values and add them to our result relation."
241+ ]
242+ },
243+ {
244+ "cell_type": "code",
245+ "execution_count": 14,
246+ "metadata": {},
247+ "outputs": [
248+ {
249+ "data": {
250+ "text/html": [
251+ "<table border=\"1\"><tr><th>s#::Text</th><th>status::Integer</th><th>statusplus10::Integer</th></tr><tr><td>\"S1\"</td><td>20</td><td>30</td></tr><tr><td>\"S4\"</td><td>20</td><td>30</td></tr><tr><td>\"S5\"</td><td>30</td><td>40</td></tr><tr><td>\"S3\"</td><td>30</td><td>40</td></tr><tr><td>\"S2\"</td><td>10</td><td>20</td></tr><tfoot><tr><td colspan=\"100%\">Finite 5 tuples</td></tr></tfoot></table>"
252+ ],
253+ "text/plain": [
254+ "┌────────┬───────────────┬─────────────────────┐\n",
255+ "│s#::Text│status::Integer│statusplus10::Integer│\n",
256+ "├────────┼───────────────┼─────────────────────┤\n",
257+ "│\"S2\" │10 │20 │\n",
258+ "│\"S3\" │30 │40 │\n",
259+ "│\"S5\" │30 │40 │\n",
260+ "│\"S4\" │20 │30 │\n",
261+ "│\"S1\" │20 │30 │\n",
262+ "└────────┴───────────────┴─────────────────────┘"
263+ ]
264+ },
265+ "metadata": {},
266+ "output_type": "display_data"
267+ }
268+ ],
269+ "source": [
270+ ":showexpr (s:{statusplus10:=add(@status,10)}){s#,status,statusplus10}"
271+ ]
272+ },
273+ {
274+ "cell_type": "markdown",
275+ "metadata": {},
276+ "source": [
277+ "### Group and Ungroup\n",
278+ "\n",
279+ "Unlike most databases, Project:M36 supports relation-valued attributes. This means that database values (atoms) can also be relations. Relation-valued attributes can be represented in the notebook as nested tables of values. Note that the nested relation's type is `relation {qty::Integer,pname::Text,s#::Text}`. \n",
280+ "\n",
281+ "Relation-valued attributes reduce tuple duplication by clearly associating certain values with their related multiple tuples. The `group` operator can also be used to create the SQL equivalent of `OUTER JOIN` but without the need for representing `NULL` in any form.\n",
282+ "\n",
283+ "Let's list the available counts for each supplier for each part."
284+ ]
285+ },
286+ {
287+ "cell_type": "code",
288+ "execution_count": 3,
289+ "metadata": {},
290+ "outputs": [
291+ {
292+ "data": {
293+ "text/html": [
294+ "<table border=\"1\"><tr><th>p#::Text</th><th>availability::relation {qty::Integer,pname::Text,s#::Text}</th></tr><tr><td>\"P6\"</td><td><table border=\"1\"><tr><th>qty::Integer</th><th>pname::Text</th><th>s#::Text</th></tr><tr><td>100</td><td>\"Cog\"</td><td>\"S1\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 1 tuples</td></tr></tfoot></table></td></tr><tr><td>\"P1\"</td><td><table border=\"1\"><tr><th>qty::Integer</th><th>pname::Text</th><th>s#::Text</th></tr><tr><td>300</td><td>\"Nut\"</td><td>\"S2\"</td></tr><tr><td>300</td><td>\"Nut\"</td><td>\"S1\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 2 tuples</td></tr></tfoot></table></td></tr><tr><td>\"P2\"</td><td><table border=\"1\"><tr><th>qty::Integer</th><th>pname::Text</th><th>s#::Text</th></tr><tr><td>200</td><td>\"Bolt\"</td><td>\"S4\"</td></tr><tr><td>200</td><td>\"Bolt\"</td><td>\"S1\"</td></tr><tr><td>400</td><td>\"Bolt\"</td><td>\"S2\"</td></tr><tr><td>200</td><td>\"Bolt\"</td><td>\"S3\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 4 tuples</td></tr></tfoot></table></td></tr><tr><td>\"P4\"</td><td><table border=\"1\"><tr><th>qty::Integer</th><th>pname::Text</th><th>s#::Text</th></tr><tr><td>200</td><td>\"Screw\"</td><td>\"S1\"</td></tr><tr><td>300</td><td>\"Screw\"</td><td>\"S4\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 2 tuples</td></tr></tfoot></table></td></tr><tr><td>\"P3\"</td><td><table border=\"1\"><tr><th>qty::Integer</th><th>pname::Text</th><th>s#::Text</th></tr><tr><td>400</td><td>\"Screw\"</td><td>\"S1\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 1 tuples</td></tr></tfoot></table></td></tr><tr><td>\"P5\"</td><td><table border=\"1\"><tr><th>qty::Integer</th><th>pname::Text</th><th>s#::Text</th></tr><tr><td>400</td><td>\"Cam\"</td><td>\"S4\"</td></tr><tr><td>100</td><td>\"Cam\"</td><td>\"S1\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 2 tuples</td></tr></tfoot></table></td></tr><tfoot><tr><td colspan=\"100%\">Finite 6 tuples</td></tr></tfoot></table>"
295+ ],
296+ "text/plain": [
297+ "┌──────────────────────────────────────────────────────────┬────────┐\n",
298+ "│availability::relation {qty::Integer,pname::Text,s#::Text}│p#::Text│\n",
299+ "├──────────────────────────────────────────────────────────┼────────┤\n",
300+ "│┌───────────┬────────────┬────────┐ │\"P5\" │\n",
301+ "││pname::Text│qty::Integer│s#::Text│ │ │\n",
302+ "│├───────────┼────────────┼────────┤ │ │\n",
303+ "││\"Cam\" │100 │\"S1\" │ │ │\n",
304+ "││\"Cam\" │400 │\"S4\" │ │ │\n",
305+ "│└───────────┴────────────┴────────┘ │ │\n",
306+ "│┌───────────┬────────────┬────────┐ │\"P3\" │\n",
307+ "││pname::Text│qty::Integer│s#::Text│ │ │\n",
308+ "│├───────────┼────────────┼────────┤ │ │\n",
309+ "││\"Screw\" │400 │\"S1\" │ │ │\n",
310+ "│└───────────┴────────────┴────────┘ │ │\n",
311+ "│┌───────────┬────────────┬────────┐ │\"P4\" │\n",
312+ "││pname::Text│qty::Integer│s#::Text│ │ │\n",
313+ "│├───────────┼────────────┼────────┤ │ │\n",
314+ "││\"Screw\" │300 │\"S4\" │ │ │\n",
315+ "││\"Screw\" │200 │\"S1\" │ │ │\n",
316+ "│└───────────┴────────────┴────────┘ │ │\n",
317+ "│┌───────────┬────────────┬────────┐ │\"P2\" │\n",
318+ "││pname::Text│qty::Integer│s#::Text│ │ │\n",
319+ "│├───────────┼────────────┼────────┤ │ │\n",
320+ "││\"Bolt\" │200 │\"S3\" │ │ │\n",
321+ "││\"Bolt\" │400 │\"S2\" │ │ │\n",
322+ "││\"Bolt\" │200 │\"S1\" │ │ │\n",
323+ "││\"Bolt\" │200 │\"S4\" │ │ │\n",
324+ "│└───────────┴────────────┴────────┘ │ │\n",
325+ "│┌───────────┬────────────┬────────┐ │\"P1\" │\n",
326+ "││pname::Text│qty::Integer│s#::Text│ │ │\n",
327+ "│├───────────┼────────────┼────────┤ │ │\n",
328+ "││\"Nut\" │300 │\"S1\" │ │ │\n",
329+ "││\"Nut\" │300 │\"S2\" │ │ │\n",
330+ "│└───────────┴────────────┴────────┘ │ │\n",
331+ "│┌───────────┬────────────┬────────┐ │\"P6\" │\n",
332+ "││pname::Text│qty::Integer│s#::Text│ │ │\n",
333+ "│├───────────┼────────────┼────────┤ │ │\n",
334+ "││\"Cog\" │100 │\"S1\" │ │ │\n",
335+ "│└───────────┴────────────┴────────┘ │ │\n",
336+ "└──────────────────────────────────────────────────────────┴────────┘"
337+ ]
338+ },
339+ "metadata": {},
340+ "output_type": "display_data"
341+ }
342+ ],
343+ "source": [
344+ ":showexpr (p join sp){qty,pname,s#,p#} group ({s#,pname,qty} as availability)"
345+ ]
346+ },
347+ {
348+ "cell_type": "markdown",
349+ "metadata": {},
350+ "source": [
351+ "We can flatten the relation-valued attributes, thereby generating more top-level tuples."
352+ ]
353+ },
354+ {
355+ "cell_type": "code",
356+ "execution_count": 4,
357+ "metadata": {},
358+ "outputs": [
359+ {
360+ "data": {
361+ "text/html": [
362+ "<table border=\"1\"><tr><th>qty::Integer</th><th>pname::Text</th><th>s#::Text</th><th>p#::Text</th></tr><tr><td>400</td><td>\"Screw\"</td><td>\"S1\"</td><td>\"P3\"</td></tr><tr><td>200</td><td>\"Bolt\"</td><td>\"S4\"</td><td>\"P2\"</td></tr><tr><td>100</td><td>\"Cog\"</td><td>\"S1\"</td><td>\"P6\"</td></tr><tr><td>100</td><td>\"Cam\"</td><td>\"S1\"</td><td>\"P5\"</td></tr><tr><td>300</td><td>\"Nut\"</td><td>\"S2\"</td><td>\"P1\"</td></tr><tr><td>400</td><td>\"Cam\"</td><td>\"S4\"</td><td>\"P5\"</td></tr><tr><td>300</td><td>\"Screw\"</td><td>\"S4\"</td><td>\"P4\"</td></tr><tr><td>200</td><td>\"Bolt\"</td><td>\"S1\"</td><td>\"P2\"</td></tr><tr><td>200</td><td>\"Screw\"</td><td>\"S1\"</td><td>\"P4\"</td></tr><tr><td>200</td><td>\"Bolt\"</td><td>\"S3\"</td><td>\"P2\"</td></tr><tr><td>300</td><td>\"Nut\"</td><td>\"S1\"</td><td>\"P1\"</td></tr><tr><td>400</td><td>\"Bolt\"</td><td>\"S2\"</td><td>\"P2\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 12 tuples</td></tr></tfoot></table>"
363+ ],
364+ "text/plain": [
365+ "┌────────┬───────────┬────────────┬────────┐\n",
366+ "│p#::Text│pname::Text│qty::Integer│s#::Text│\n",
367+ "├────────┼───────────┼────────────┼────────┤\n",
368+ "│\"P2\" │\"Bolt\" │400 │\"S2\" │\n",
369+ "│\"P1\" │\"Nut\" │300 │\"S1\" │\n",
370+ "│\"P2\" │\"Bolt\" │200 │\"S3\" │\n",
371+ "│\"P4\" │\"Screw\" │200 │\"S1\" │\n",
372+ "│\"P2\" │\"Bolt\" │200 │\"S1\" │\n",
373+ "│\"P4\" │\"Screw\" │300 │\"S4\" │\n",
374+ "│\"P5\" │\"Cam\" │400 │\"S4\" │\n",
375+ "│\"P1\" │\"Nut\" │300 │\"S2\" │\n",
376+ "│\"P5\" │\"Cam\" │100 │\"S1\" │\n",
377+ "│\"P6\" │\"Cog\" │100 │\"S1\" │\n",
378+ "│\"P2\" │\"Bolt\" │200 │\"S4\" │\n",
379+ "│\"P3\" │\"Screw\" │400 │\"S1\" │\n",
380+ "└────────┴───────────┴────────────┴────────┘"
381+ ]
382+ },
383+ "metadata": {},
384+ "output_type": "display_data"
385+ }
386+ ],
387+ "source": [
388+ ":showexpr ((p join sp){qty,pname,s#,p#} group ({s#,pname,qty} as availability)) ungroup availability"
389+ ]
390+ },
391+ {
392+ "cell_type": "markdown",
393+ "metadata": {},
394+ "source": [
395+ "## Database State Operators\n",
396+ "\n",
397+ "Thus far, we have only been inspecting the database state. Next, let's cover some database update operators. These operators are all similar to SQL operators, so we don't need a lot of exposition."
398+ ]
399+ },
400+ {
401+ "cell_type": "markdown",
402+ "metadata": {},
403+ "source": [
404+ "### Insert"
405+ ]
406+ },
407+ {
408+ "cell_type": "code",
409+ "execution_count": 6,
410+ "metadata": {},
411+ "outputs": [
412+ {
413+ "data": {
414+ "text/html": [
415+ "ok"
416+ ],
417+ "text/plain": [
418+ "ok"
419+ ]
420+ },
421+ "metadata": {},
422+ "output_type": "display_data"
423+ }
424+ ],
425+ "source": [
426+ "insert s relation{tuple{s# \"S6\", sname \"Bob\", status 50, city \"Boston\"}}"
427+ ]
428+ },
429+ {
430+ "cell_type": "code",
431+ "execution_count": 7,
432+ "metadata": {},
433+ "outputs": [
434+ {
435+ "data": {
436+ "text/html": [
437+ "<table border=\"1\"><tr><th>city::Text</th><th>s#::Text</th><th>sname::Text</th><th>status::Integer</th></tr><tr><td>\"Boston\"</td><td>\"S6\"</td><td>\"Bob\"</td><td>50</td></tr><tr><td>\"Paris\"</td><td>\"S3\"</td><td>\"Blake\"</td><td>30</td></tr><tr><td>\"Athens\"</td><td>\"S5\"</td><td>\"Adams\"</td><td>30</td></tr><tr><td>\"London\"</td><td>\"S4\"</td><td>\"Clark\"</td><td>20</td></tr><tr><td>\"London\"</td><td>\"S1\"</td><td>\"Smith\"</td><td>20</td></tr><tr><td>\"Paris\"</td><td>\"S2\"</td><td>\"Jones\"</td><td>10</td></tr><tfoot><tr><td colspan=\"100%\">Finite 6 tuples</td></tr></tfoot></table>"
438+ ],
439+ "text/plain": [
440+ "┌──────────┬────────┬───────────┬───────────────┐\n",
441+ "│city::Text│s#::Text│sname::Text│status::Integer│\n",
442+ "├──────────┼────────┼───────────┼───────────────┤\n",
443+ "│\"Paris\" │\"S2\" │\"Jones\" │10 │\n",
444+ "│\"London\" │\"S1\" │\"Smith\" │20 │\n",
445+ "│\"London\" │\"S4\" │\"Clark\" │20 │\n",
446+ "│\"Athens\" │\"S5\" │\"Adams\" │30 │\n",
447+ "│\"Paris\" │\"S3\" │\"Blake\" │30 │\n",
448+ "│\"Boston\" │\"S6\" │\"Bob\" │50 │\n",
449+ "└──────────┴────────┴───────────┴───────────────┘"
450+ ]
451+ },
452+ "metadata": {},
453+ "output_type": "display_data"
454+ }
455+ ],
456+ "source": [
457+ ":showexpr s"
458+ ]
459+ },
460+ {
461+ "cell_type": "markdown",
462+ "metadata": {},
463+ "source": [
464+ "### Delete"
465+ ]
466+ },
467+ {
468+ "cell_type": "code",
469+ "execution_count": 9,
470+ "metadata": {},
471+ "outputs": [
472+ {
473+ "data": {
474+ "text/html": [
475+ "ok"
476+ ],
477+ "text/plain": [
478+ "ok"
479+ ]
480+ },
481+ "metadata": {},
482+ "output_type": "display_data"
483+ }
484+ ],
485+ "source": [
486+ "delete s where sname=\"Bob\""
487+ ]
488+ },
489+ {
490+ "cell_type": "code",
491+ "execution_count": 10,
492+ "metadata": {},
493+ "outputs": [
494+ {
495+ "data": {
496+ "text/html": [
497+ "<table border=\"1\"><tr><th>city::Text</th><th>s#::Text</th><th>sname::Text</th><th>status::Integer</th></tr><tr><td>\"Paris\"</td><td>\"S3\"</td><td>\"Blake\"</td><td>30</td></tr><tr><td>\"Athens\"</td><td>\"S5\"</td><td>\"Adams\"</td><td>30</td></tr><tr><td>\"London\"</td><td>\"S4\"</td><td>\"Clark\"</td><td>20</td></tr><tr><td>\"London\"</td><td>\"S1\"</td><td>\"Smith\"</td><td>20</td></tr><tr><td>\"Paris\"</td><td>\"S2\"</td><td>\"Jones\"</td><td>10</td></tr><tfoot><tr><td colspan=\"100%\">Finite 5 tuples</td></tr></tfoot></table>"
498+ ],
499+ "text/plain": [
500+ "┌──────────┬────────┬───────────┬───────────────┐\n",
501+ "│city::Text│s#::Text│sname::Text│status::Integer│\n",
502+ "├──────────┼────────┼───────────┼───────────────┤\n",
503+ "│\"Paris\" │\"S2\" │\"Jones\" │10 │\n",
504+ "│\"London\" │\"S1\" │\"Smith\" │20 │\n",
505+ "│\"London\" │\"S4\" │\"Clark\" │20 │\n",
506+ "│\"Athens\" │\"S5\" │\"Adams\" │30 │\n",
507+ "│\"Paris\" │\"S3\" │\"Blake\" │30 │\n",
508+ "└──────────┴────────┴───────────┴───────────────┘"
509+ ]
510+ },
511+ "metadata": {},
512+ "output_type": "display_data"
513+ }
514+ ],
515+ "source": [
516+ ":showexpr s"
517+ ]
518+ },
519+ {
520+ "cell_type": "markdown",
521+ "metadata": {},
522+ "source": [
523+ "### Update"
524+ ]
525+ },
526+ {
527+ "cell_type": "code",
528+ "execution_count": 11,
529+ "metadata": {},
530+ "outputs": [
531+ {
532+ "data": {
533+ "text/html": [
534+ "ok"
535+ ],
536+ "text/plain": [
537+ "ok"
538+ ]
539+ },
540+ "metadata": {},
541+ "output_type": "display_data"
542+ }
543+ ],
544+ "source": [
545+ "update s where city=\"London\" (status:=90)"
546+ ]
547+ },
548+ {
549+ "cell_type": "code",
550+ "execution_count": 12,
551+ "metadata": {},
552+ "outputs": [
553+ {
554+ "data": {
555+ "text/html": [
556+ "<table border=\"1\"><tr><th>city::Text</th><th>s#::Text</th><th>sname::Text</th><th>status::Integer</th></tr><tr><td>\"London\"</td><td>\"S1\"</td><td>\"Smith\"</td><td>90</td></tr><tr><td>\"Paris\"</td><td>\"S3\"</td><td>\"Blake\"</td><td>30</td></tr><tr><td>\"Athens\"</td><td>\"S5\"</td><td>\"Adams\"</td><td>30</td></tr><tr><td>\"London\"</td><td>\"S4\"</td><td>\"Clark\"</td><td>90</td></tr><tr><td>\"Paris\"</td><td>\"S2\"</td><td>\"Jones\"</td><td>10</td></tr><tfoot><tr><td colspan=\"100%\">Finite 5 tuples</td></tr></tfoot></table>"
557+ ],
558+ "text/plain": [
559+ "┌──────────┬────────┬───────────┬───────────────┐\n",
560+ "│city::Text│s#::Text│sname::Text│status::Integer│\n",
561+ "├──────────┼────────┼───────────┼───────────────┤\n",
562+ "│\"Paris\" │\"S2\" │\"Jones\" │10 │\n",
563+ "│\"London\" │\"S4\" │\"Clark\" │90 │\n",
564+ "│\"Athens\" │\"S5\" │\"Adams\" │30 │\n",
565+ "│\"Paris\" │\"S3\" │\"Blake\" │30 │\n",
566+ "│\"London\" │\"S1\" │\"Smith\" │90 │\n",
567+ "└──────────┴────────┴───────────┴───────────────┘"
568+ ]
569+ },
570+ "metadata": {},
571+ "output_type": "display_data"
572+ }
573+ ],
574+ "source": [
575+ ":showexpr s"
576+ ]
577+ },
578+ {
579+ "cell_type": "markdown",
580+ "metadata": {},
581+ "source": [
582+ "### Assign"
583+ ]
584+ },
585+ {
586+ "cell_type": "code",
587+ "execution_count": 13,
588+ "metadata": {},
589+ "outputs": [
590+ {
591+ "data": {
592+ "text/html": [
593+ "ok"
594+ ],
595+ "text/plain": [
596+ "ok"
597+ ]
598+ },
599+ "metadata": {},
600+ "output_type": "display_data"
601+ }
602+ ],
603+ "source": [
604+ "london_suppliers := s where city=\"London\""
605+ ]
606+ },
607+ {
608+ "cell_type": "code",
609+ "execution_count": 14,
610+ "metadata": {},
611+ "outputs": [
612+ {
613+ "data": {
614+ "text/html": [
615+ "<table border=\"1\"><tr><th>city::Text</th><th>s#::Text</th><th>sname::Text</th><th>status::Integer</th></tr><tr><td>\"London\"</td><td>\"S1\"</td><td>\"Smith\"</td><td>90</td></tr><tr><td>\"London\"</td><td>\"S4\"</td><td>\"Clark\"</td><td>90</td></tr><tfoot><tr><td colspan=\"100%\">Finite 2 tuples</td></tr></tfoot></table>"
616+ ],
617+ "text/plain": [
618+ "┌──────────┬────────┬───────────┬───────────────┐\n",
619+ "│city::Text│s#::Text│sname::Text│status::Integer│\n",
620+ "├──────────┼────────┼───────────┼───────────────┤\n",
621+ "│\"London\" │\"S4\" │\"Clark\" │90 │\n",
622+ "│\"London\" │\"S1\" │\"Smith\" │90 │\n",
623+ "└──────────┴────────┴───────────┴───────────────┘"
624+ ]
625+ },
626+ "metadata": {},
627+ "output_type": "display_data"
628+ }
629+ ],
630+ "source": [
631+ ":showexpr london_suppliers"
632+ ]
633+ },
634+ {
635+ "cell_type": "markdown",
636+ "metadata": {},
637+ "source": [
638+ "## Constraints\n",
639+ "\n",
640+ "Project:M36 supports too many features to cover in one notebook, but constraints are certainly essential.\n",
641+ "\n",
642+ "Let's examine the current constraints."
643+ ]
644+ },
645+ {
646+ "cell_type": "code",
647+ "execution_count": 16,
648+ "metadata": {},
649+ "outputs": [
650+ {
651+ "data": {
652+ "text/html": [
653+ "<table border=\"1\"><tr><th>name::Text</th><th>sub::Text</th><th>super::Text</th></tr><tr><td>\"p_pkey\"</td><td>\"NotEquals (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (RelationVariable \\\"p\\\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (Project (AttributeNames (fromList [\\\"p#\\\"])) (RelationVariable \\\"p\\\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))\"</td><td>\"ExistingRelation (Relation [] (RelationTupleSet {asList = []}))\"</td></tr><tr><td>\"s_pkey\"</td><td>\"NotEquals (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (RelationVariable \\\"s\\\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (Project (AttributeNames (fromList [\\\"s#\\\"])) (RelationVariable \\\"s\\\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))\"</td><td>\"ExistingRelation (Relation [] (RelationTupleSet {asList = []}))\"</td></tr><tr><td>\"sp_pkey\"</td><td>\"NotEquals (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (RelationVariable \\\"sp\\\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (Project (AttributeNames (fromList [\\\"p#\\\",\\\"s#\\\"])) (RelationVariable \\\"sp\\\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))\"</td><td>\"ExistingRelation (Relation [] (RelationTupleSet {asList = []}))\"</td></tr><tfoot><tr><td colspan=\"100%\">Finite 3 tuples</td></tr></tfoot></table>"
654+ ],
655+ "text/plain": [
656+ "┌──────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────┐\n",
657+ "│name::Text│sub::Text │super::Text │\n",
658+ "├──────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────┤\n",
659+ "│\"sp_pkey\" │\"NotEquals (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (RelationVariable \\\"sp\\\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (Project (AttributeNames (fromList [\\\"p#\\\",\\\"s#\\\"])) (RelationVariable \\\"sp\\\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))\"│\"ExistingRelation (Relation [] (RelationTupleSet {asList = []}))\"│\n",
660+ "│\"s_pkey\" │\"NotEquals (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (RelationVariable \\\"s\\\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (Project (AttributeNames (fromList [\\\"s#\\\"])) (RelationVariable \\\"s\\\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))\" │\"ExistingRelation (Relation [] (RelationTupleSet {asList = []}))\"│\n",
661+ "│\"p_pkey\" │\"NotEquals (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (RelationVariable \\\"p\\\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\\\"b\\\"])) (Extend (AttributeExtendTupleExpr \\\"b\\\" (FunctionAtomExpr \\\"count\\\" [AttributeAtomExpr \\\"a\\\"] ())) (Extend (AttributeExtendTupleExpr \\\"a\\\" (RelationAtomExpr (Project (AttributeNames (fromList [\\\"p#\\\"])) (RelationVariable \\\"p\\\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))\" │\"ExistingRelation (Relation [] (RelationTupleSet {asList = []}))\"│\n",
662+ "└──────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────┘"
663+ ]
664+ },
665+ "metadata": {},
666+ "output_type": "display_data"
667+ }
668+ ],
669+ "source": [
670+ ":constraints"
671+ ]
672+ },
673+ {
674+ "cell_type": "markdown",
675+ "metadata": {},
676+ "source": [
677+ "Constraints are represented as \"inclusion dependencies\". An inclusion dependency is a generalization of all possible constraints on the database and can represent keys, uniqueness constraints, foreign key constraints, as well as span multiple relation variables.\n",
678+ "\n",
679+ "An inclusion dependency requires that the result of relational expression `sub` is a subset of relational expression `super`.\n",
680+ "\n",
681+ "Project:M36 includes some utilities to generate inclusion dependencies."
682+ ]
683+ },
684+ {
685+ "cell_type": "code",
686+ "execution_count": 18,
687+ "metadata": {},
688+ "outputs": [
689+ {
690+ "data": {
691+ "text/html": [
692+ "ok"
693+ ],
694+ "text/plain": [
695+ "ok"
696+ ]
697+ },
698+ "metadata": {},
699+ "output_type": "display_data"
700+ }
701+ ],
702+ "source": [
703+ "sandwich := relation{tuple{name \"Cheese\", price 4},tuple{name \"Tuna\", price 5}}"
704+ ]
705+ },
706+ {
707+ "cell_type": "code",
708+ "execution_count": 20,
709+ "metadata": {},
710+ "outputs": [
711+ {
712+ "data": {
713+ "text/html": [
714+ "ok"
715+ ],
716+ "text/plain": [
717+ "ok"
718+ ]
719+ },
720+ "metadata": {},
721+ "output_type": "display_data"
722+ }
723+ ],
724+ "source": [
725+ "key sandwich_name{name} sandwich"
726+ ]
727+ },
728+ {
729+ "cell_type": "code",
730+ "execution_count": 22,
731+ "metadata": {},
732+ "outputs": [
733+ {
734+ "ename": "TutorialDError",
735+ "evalue": "",
736+ "output_type": "error",
737+ "traceback": [
738+ "InclusionDependencyCheckError \"sandwich_name\""
739+ ]
740+ }
741+ ],
742+ "source": [
743+ "insert sandwich relation{tuple{name \"Tuna\", price 6}}"
744+ ]
745+ },
746+ {
747+ "cell_type": "markdown",
748+ "metadata": {},
749+ "source": [
750+ "## Jupyter Kernel Features\n",
751+ "\n",
752+ "By default, the TutorialD kernel will automatically start a `project-m36-websocket-server` when the first TutorialD command is executed. For this to work, `project-m36-websocket-server` must be in the jupyter server's `PATH` environment variable.\n",
753+ "\n",
754+ "### Magic\n",
755+ "\n",
756+ "Like the python kernel, the TutorialD kernel includes some commands to interact directly with the kernel. These commands are not passed to the TutorialD interpreter.\n",
757+ "\n",
758+ "* `%help` brings up some help which includes links to additional documentation\n",
759+ "* `%connect ws://<host>:<port> <dbname>` connects to the `project-m36-websocket-server` already running. For example:\n",
760+ " ```\n",
761+ " %connect ws://localhost:64000 employees\n",
762+ " ```\n",
763+ " connects to the websocket server running on the localhost on port 64000 and selects the database named \"employees\". Note that the database name is not part of the websocket URL.\n",
764+ " \n",
765+ "## Conclusion\n",
766+ "\n",
767+ "[Project:M36](https://github.com/agentm/project-m36) is a fully-featured relational algebra engine suitable for use a database. It sets out to prove that sticking to the mathematical underpinnings of the relational algebra is straightforward and results in improved correctness and performance.\n",
768+ "\n",
769+ "If you would like to learn more, please refer to the [documentation](https://github.com/agentm/project-m36#documentation) and join our welcoming [community](https://github.com/agentm/project-m36#community) to ask questions."
770+ ]
771+ },
772+ {
773+ "cell_type": "code",
774+ "execution_count": null,
775+ "metadata": {},
776+ "outputs": [],
777+ "source": []
778+ }
779+ ],
780+ "metadata": {
781+ "kernelspec": {
782+ "display_name": "TutorialD (Project:M36)",
783+ "language": "TutorialD",
784+ "name": "tutd"
785+ },
786+ "language_info": {
787+ "file_extension": ".tutd",
788+ "mimetype": "text/x-tutd",
789+ "name": "TutorialD"
790+ }
791+ },
792+ "nbformat": 4,
793+ "nbformat_minor": 2
794+}
jupyter/itutd/ITutorialDKernel.py+104−39
@@ -1,11 +1,12 @@
11from ipykernel.kernelbase import Kernel
22import subprocess
3−from websocket import create_connection
3+from websocket import create_connection, WebSocketConnectionClosedException
44import time
55import select
66import json
77import socket
88import errno
9+import urlparse
910
1011class ITutorialDKernel(Kernel):
1112 implementation = 'ITutorialD'
@@ -23,45 +24,61 @@ class ITutorialDKernel(Kernel):
2324 dbserver = None
2425 ws = None
2526
27+# todo: implement wrong database name handling
2628 def runtutd(self, tutd):
2729 #setup server
28− port = 63000
2930 dbname = 'jupyter'
30− if not self.dbserver:
31− self.dbserver = subprocess.Popen(['project-m36-websocket-server',
32− '--database', dbname,
33− '--port', str(port)],
34− )
35− if not self.ws:
36− attempts = 0
37− while 1:
38− try:
39− ws = create_connection('ws://127.0.0.1:{}'.format(port))
40− except socket.error as err:
41− if err.errno in (errno.ECONNREFUSED, ):
42− time.sleep(0.3)
43− attempts += 1
44− if attempts > 10:
45− assert False, "websocket timeout"
46− else:
47− raise
31+ port = 63000
32+ if not self.ws and not self.dbserver:
33+ try:
34+ self.dbserver = subprocess.Popen(['project-m36-websocket-server',
35+ '--database', dbname,
36+ '--port', str(port)],
37+ )
38+ except OSError as err:
39+ if err.errno == errno.ENOENT:
40+ return ('"project-m36-websocket-server" is not in the PATH environment variable and could not be started.', None)
4841 else:
49− break
50− self.ws = ws
51− self.ws.send('connectdb:{}'.format(dbname))
52− #receive and discard prompt info
53− self.ws.recv()
54− self.ws.recv()
42+ raise
43+ res = self.connect_ws('ws://127.0.0.1:63000', dbname, retries=10)
44+ if res:
45+ return ('Connection attempt failed.', None)
46+
5547 self.ws.send('executetutd/html+text:' + tutd)
56− jsonresult = self.discard_prompts()
48+ try:
49+ jsonresult = self.discard_prompts()
50+ except WebSocketConnectionClosedException:
51+ self.ws = None
52+ return ('Websocket server closed the connection.',None)
5753 res = json.loads(jsonresult)
58− if 'displayerror' in jsonresult:
59− return (res, None)
60− elif 'acknowledged' in jsonresult:
54+ if 'displayerror' in res:
55+ return (res['displayerror']['text'], None)
56+ elif 'acknowledged' in res:
6157 return (None, {'html':'ok', 'text':'ok'})
6258 else:
6359 return (None, res['displayrelation'])
6460
61+ def connect_ws(self, url, dbname, retries=0):
62+ while 1:
63+ try:
64+ ws = create_connection(url)
65+ except socket.error as err:
66+ if err.errno in (errno.ECONNREFUSED, ):
67+ time.sleep(0.3)
68+ if retries == 0:
69+ return 'Failed to connect to websocket.'
70+ retries -= 1
71+ else:
72+ raise
73+ else:
74+ break
75+ self.ws = ws
76+ self.ws.send('connectdb:{}'.format(dbname))
77+ msg = json.loads(self.ws.recv())
78+ if 'displayerror' in msg:
79+ return msg['displayerror']['tag']
80+ return None
81+
6582 def discard_prompts(self):
6683 #read from the websocket until a non-prompt message is received
6784 while 1:
@@ -72,31 +89,61 @@ class ITutorialDKernel(Kernel):
7289 if 'promptInfo' not in msg:
7390 return msg
7491
92+ def send_error(self, msg):
93+ stream_content = {'ename':'TutorialDError',
94+ 'traceback':[msg],
95+ 'evalue':''}
96+ self.send_response(self.iopub_socket, 'error', stream_content)
97+
98+
99+ def process_magic(self, code):
100+ #handle "magic" processing of commands which are not sent to the tutd interpreter
101+ cmds = code.split(' ')
102+ ok = {'text':'ok', 'html':'ok'}
103+
104+ if cmds[0] == '%connect' and len(cmds) == 3:
105+ #connect to alternate database
106+ url = urlparse.urlparse(cmds[1])
107+ dbname = cmds[2]
108+ if not url.scheme.startswith('ws'):
109+ return ('Rejected attempt to connect to non-websocket. Use ws:// or wss:// scheme',None)
110+ res = self.connect_ws(url.geturl(), dbname)
111+ if res: #error from connection
112+ return (res, None, None)
113+ else: #connection successful
114+ return (None, ok, None)
115+ elif cmds[0] == '%help' and len(cmds) == 1:
116+ #display some help
117+ return (None, ok, self.show_help())
118+ elif cmds[0].startswith('%'):
119+ return ('Invalid magic command.', None)
120+
75121 def do_execute(self, code, silent,
76122 store_history=True,
77123 user_expressions=False,
78124 allow_stdin=False):
79− (err, relresult) = self.runtutd(code)
125+ magic = self.process_magic(code)
126+ payload = None
127+ if magic:
128+ (err, result, payload) = magic
129+ else:
130+ (err, result) = self.runtutd(code)
80131 if not silent:
81132 if err:
82133 status = 'error'
83− stream_content = {'ename':'TutorialDError',
84− 'traceback':[err['displayerror']['html']],
85− 'ename':'TutorialDError',
86− 'evalue':''}
87− self.send_response(self.iopub_socket, 'error', stream_content)
134+ self.send_error(err)
88135 else:
89136 status = 'ok'
90137 display_content = {
91− 'data':{'text/html':relresult['html'],
92− 'text/plain':relresult['text']},
138+ 'data':{'text/html':result['html'],
139+ 'text/plain':result['text']},
93140 'metadata':{},
94141 'transient':{}
95142 }
96143 self.send_response(self.iopub_socket, 'display_data', display_content)
97144 return {'status': status,
98145 'execution_count': self.execution_count,
99− 'payload':[],
146+ 'payload':[payload],
100147 'user_expressions':{}}
101148
102149 def do_shutdown(self, restart):
@@ -104,6 +151,24 @@ class ITutorialDKernel(Kernel):
104151 self.dbserver.terminate()
105152 self.dbserver.kill()
106153
154+ def show_help(self):
155+ return {'source': 'page',
156+ 'start':0,
157+ 'data':{'text/html':'''<h1>Project:M36 TutorialD Jupyter Kernel Help</h1>
158+<p>TutorialD is an interactive language for the relational algebra.</p>
159+
160+<p><a href="https://github.com/agentm/project-m36/blob/master/docs/15_minute_tutorial.markdown">15 Minute TutorialD Introduction</a></p>
161+<p><a href="https://github.com/agentm/project-m36/blob/master/docs/tutd_tutorial.markdown">TutorialD Documentation</a></p>
162+''',
163+ 'text/plain':
164+'''Project:M36 TutorialD Jupyter Kernel Help
165+
166+TutorialD is an interactive language for the relational algebra.
167+
168+15 Minute TutorialD Introduction: https://github.com/agentm/project-m36/blob/master/docs/15_minute_tutorial.markdown
169+TutorialD Documentation: https://github.com/agentm/project-m36/blob/master/docs/tutd_tutorial.markdown
170+'''}}
171+
107172if __name__ == '__main__':
108173 from ipykernel.kernelapp import IPKernelApp
109174 IPKernelApp.launch_instance(kernel_class=ITutorialDKernel)
jupyter/setup.py+1−1
@@ -1,4 +1,4 @@
1−from distutils.core import setup
1+from setuptools import setup
22
33setup(
44 name='itutd',
src/lib/ProjectM36/MiscUtils.hs+6
@@ -7,3 +7,9 @@ dupes [_] = []
77dupes [x,y] = [x | x == y]
88dupes (x:y:xs) = dupes(x:[y]) ++ dupes(y : xs)
99
10+--Data.Vector.indexed but for lists
11+indexed :: [a] -> [(Int, a)]
12+indexed = go 0
13+ where
14+ go _ [] = []
15+ go i (v:ys) = (i,v):go (i+1) ys
src/lib/ProjectM36/Relation/Show/HTML.hs+6−5
@@ -5,17 +5,16 @@ import ProjectM36.Tuple
55import ProjectM36.Atom
66import ProjectM36.AtomType
77import qualified Data.List as L
8−import ProjectM36.Attribute
98import Data.Text (append, Text, pack)
109import qualified Data.Text as T
1110import qualified Data.Text.IO as TIO
1211import Data.Monoid
12+import qualified Data.Vector as V
1313
1414attributesAsHTML :: Attributes -> Text
15−attributesAsHTML attrs = "<tr>" `append` T.concat (map oneAttrHTML sortedAttrs) `append` "</tr>"
15+attributesAsHTML attrs = "<tr>" `append` T.concat (map oneAttrHTML (V.toList attrs)) `append` "</tr>"
1616 where
1717 oneAttrHTML attr = "<th>" <> prettyAttribute attr <> "</th>"
18− sortedAttrs = orderedAttributes attrs
1918
2019relationAsHTML :: Relation -> Text
2120relationAsHTML rel@(Relation attrNameSet tupleSet) = "<table border=\"1\">" `append` attributesAsHTML attrNameSet `append` tupleSetAsHTML tupleSet `append` "<tfoot>" `append` tablefooter `append` "</tfoot></table>"
@@ -29,9 +28,11 @@ writeRel :: Relation -> IO ()
2928writeRel = writeHTML . relationAsHTML
3029
3130tupleAsHTML :: RelationTuple -> Text
32−tupleAsHTML tuple = "<tr>" `append` T.concat (L.map tupleFrag (tupleSortedAssocs tuple)) `append` "</tr>"
31+tupleAsHTML tuple = "<tr>" `append` T.concat (L.map tupleFrag (tupleAssocs tuple)) `append` "</tr>"
3332 where
34− tupleFrag tup = "<td>" `append` atomToText (snd tup) `append` "</td>"
33+ tupleFrag tup = "<td>" `append` atomAsHTML (snd tup) `append` "</td>"
34+ atomAsHTML (RelationAtom rel) = relationAsHTML rel
35+ atomAsHTML atom = atomToText atom
3536
3637tupleSetAsHTML :: RelationTupleSet -> Text
3738tupleSetAsHTML tupSet = foldr folder "" (asList tupSet)
src/lib/ProjectM36/Tuple.hs+8−4
@@ -29,6 +29,14 @@ tupleAttributes (RelationTuple tupAttrs _) = tupAttrs
2929tupleAssocs :: RelationTuple -> [(AttributeName, Atom)]
3030tupleAssocs (RelationTuple attrVec tupVec) = V.toList $ V.map (first attributeName) (V.zip attrVec tupVec)
3131
32+orderedTupleAssocs :: RelationTuple -> [(AttributeName, Atom)]
33+orderedTupleAssocs tup@(RelationTuple attrVec _) = map (\attr -> (attributeName attr, atomForAttr (attributeName attr))) oAttrs
34+ where
35+ oAttrs = orderedAttributes attrVec
36+ atomForAttr nam = case atomForAttributeName nam tup of
37+ Left _ -> TextAtom "<?>"
38+ Right val -> val
39+
3240-- return atoms in some arbitrary but consistent key order
3341tupleAtoms :: RelationTuple -> V.Vector Atom
3442tupleAtoms (RelationTuple _ tupVec) = tupVec
@@ -146,10 +154,6 @@ tupleAtomExtend newAttrName atom tupIn = tupleExtend tupIn newTup
146154 where
147155 newTup = RelationTuple (V.singleton $ Attribute newAttrName (atomTypeForAtom atom)) (V.singleton atom)
148156
149−{- sort the associative list to match the header sorting -}
150−tupleSortedAssocs :: RelationTuple -> [(AttributeName, Atom)]
151−tupleSortedAssocs (RelationTuple tupAttrs tupVec) = V.toList $ V.map (\(index,attr) -> (attributeName attr, tupVec V.! index)) $ V.indexed tupAttrs
152−
153157--this could be cheaper- it may not be wortwhile to update all the tuples for projection, but then the attribute management must be slightly different- perhaps the attributes should be a vector of association tuples [(name, index)]
154158tupleProject :: S.Set AttributeName -> RelationTuple -> RelationTuple
155159tupleProject projectAttrs (RelationTuple attrs tupVec) = RelationTuple newAttrs newTupVec