Merged honey-redux into arxana-merge.
[arxana.git] / latex / arxana-merge.tex
blob33c026e073aa9e54b9c255e3d16a6509b50b3099
1 %;; Copyright (C) 2005-2017 Joe Corneli <holtzermann17@gmail.com>
2 %;; Copyright (C) 2010-2017 Ray Puzio <rsp@novres.org>
4 %;; This program is free software: you can redistribute it and/or modify
5 %;; it under the terms of the GNU Affero General Public License as published by
6 %;; the Free Software Foundation, either version 3 of the License, or
7 %;; (at your option) any later version.
8 %;;
9 %;; This program is distributed in the hope that it will be useful,
10 %;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 %;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 %;; GNU Affero General Public License for more details.
13 %;;
14 %;; You should have received a copy of the GNU Affero General Public License
15 %;; along with this program. If not, see <http://www.gnu.org/licenses/>.
17 % (progn
18 % (find-file "~/arxana.tex")
19 % (save-excursion
20 % (goto-char (point-max))
21 % (let ((beg (progn (search-backward "\\begin{verbatim}")
22 % (match-end 0)))
23 % (end (progn (search-forward "\\end{verbatim}")
24 % (match-beginning 0))))
25 % (eval-region beg end)
26 % (lit-eval))))
28 %%% Commentary:
30 %% To load: remove %'s above and evaluate with C-x C-e.
32 %% Alternatively, run this:
33 % head -n 13 arxana.tex | sed -e "/%/s///" > arxana-loader.el
34 %% on the command line to produce something you can use
35 %% to load Arxana when you start Emacs:
36 % emacs -l arxana-loader.el
38 %% Or put the expression in your ~/.emacs (perhaps wrapped
39 %% in function like `eval-arxana').
41 %% Or search for a similar form below and evaluate there!
43 %%% Code:
45 \documentclass{article}
47 \usepackage[T1]{fontenc}
48 \usepackage{textcomp}
50 \usepackage{hyperref}
51 \usepackage{amsmath}
52 \usepackage{amsthm}
53 \usepackage{verbatim}
55 \newcommand{\todo}[1]{\footnote{\bf TODO: #1}}
56 \newcommand{\meta}[1]{$\langle${\it #1}$\rangle$}
58 \theoremstyle{definition}
59 \newtheorem{nota}{Note}[section]
61 \parindent = 1.2em
63 \newenvironment{notate}[1]
64 {\begin{nota}[{\bf {\em #1}}]}%
65 {\end{nota}}
67 \makeatletter
68 \newenvironment{elisp}
69 {\let\ORGverbatim@font\verbatim@font
70 \def\verbatim@font{\ttfamily\scshape}%
71 \verbatim}
72 {\endverbatim
73 \let\verbatim@font\ORGverbatim@font}
74 \makeatother
76 \makeatletter
77 \newenvironment{idea}
78 {\let\ORGverbatim@font\verbatim@font
79 \def\verbatim@font{\ttfamily\slshape}%
80 \verbatim}
81 {\endverbatim
82 \let\verbatim@font\ORGverbatim@font}
83 \makeatother
85 \begin{document}
87 \title{\emph{Arxana 2017}}
89 \author{Joseph Corneli \& Raymond Puzio\thanks{Copyright (C) 2005-2017
90 Joseph Corneli {\tt <holtzermann17@gmail.com>}\newline
91 Copyright (C) 2010-2017 Raymond Puzio {\tt <rsp@novres.org>}\newline
92 $\longrightarrow$ transferred to the public domain.}}
93 \date{Last revised: \today}
95 \maketitle
97 \abstract{A tool for building hackable semantic hypertext platforms.
98 An overview and link to our source code repository is at {\tt
99 http://arxana.net}.}
101 \tableofcontents
103 \section{Introduction}
105 \begin{notate}{What is ``Arxana''?} \label{arxana}
106 \emph{Arxana} is the name of a ``next generation'' hypertext system
107 that emphasizes annotation. Every object in this system is
108 annotatable. Because of this, our first name for the program ``the
109 scholium system'', but ``Arxana'' better reflects our aim: to explore
110 the mysterious world of links, attachments, correspondences, and
111 side-effects. This edition of the program relies entirely on Emacs
112 for storage and display, and integrates a backend storage mechanism
113 devised by Ray Puzio, and frontend interactions from earlier
114 prototypes by Joe Corneli, and (in Section \ref{farm-demo}) a new
115 application to modelling mathematical dialogues jointly developed by
116 both authors. Previous versions contain often excessive but
117 sometimes interesting discussion of ideas for future work.
118 Such discussion has been kept to a minimum here.
119 \end{notate}
121 \begin{notate}{Using the program}
122 If you are looking at the source version of this document
123 in Emacs, evaluate the following s-expression (type
124 \emph{C-x C-e} with the cursor positioned just after its
125 final parenthesis). This prepares the Emacs environment
126 for interactive use. (The code that achieves this is
127 in Appendix \ref{appendix-lit}.)
129 \begin{idea}
130 (save-excursion
131 (let ((beg (search-forward "\\begin{verbatim}"))
132 (end (progn (search-forward "\\end{verbatim}")
133 (match-beginning 0))))
134 (eval-region beg end)
135 (lit-process)))
136 \end{idea}
138 If you are looking at this in a PDF or printout, you will see that the
139 document has a ``literate'' style. That is, it can be read as text,
140 not just as code. In Section \ref{frontend}, we define functions that
141 allow the user to read and revise the contents of this document as
142 hypertext.
143 \end{notate}
145 \section{Backend}
147 \begin{notate}{Overview of the backend}
148 This backend is a Higher Order NEtwork Yarnknotter or HONEY for short.
149 It stores a \emph{network} of quintuplets of the form {\tt(uid label
150 source sink . content)}. One such quintuplet is called a
151 \emph{nema}. The structure of an individual nema is as follows: The
152 {\tt uid} is a numeric identifier that is unique on a network
153 basis. The {\tt label} is an (optional) string that corresponds
154 uniquely to the uid. The {\tt source} is the nema (identified by
155 uid) to the ``left'' of our quintuplet, and the {\tt sink} is the
156 uid to the ``right'' of our quintuplet. One or both may be {\tt
157 0}, and in case both are, the nema is understood to be a
158 \emph{node}. Lastly, the nema's {\tt content} can be any Lisp
159 object -- including another nema in the same network or another
160 network.\todo{(Check this last statement...)}
161 \end{notate}
163 \subsection{Preliminaries}
165 \begin{notate}{Some preliminaries}
166 We define several simple preliminary functions that we use later on.
167 \end{notate}
169 \begin{notate}{Required packages}
170 We use the Common Lisp compatibility functions.
171 \end{notate}
173 \begin{elisp}
174 (require 'cl)
175 \end{elisp}
177 \begin{notate}{On `filter'}
178 This is a useful utility to filter elements of a list satisfying a condition.
179 Returns the subset of stuff which satisfies the predicate pred.
180 \end{notate}
182 \begin{elisp}
183 (defun filter (pred stuff)
184 (let ((ans nil))
185 (dolist (item stuff (reverse ans))
186 (if (funcall pred item)
187 (setq ans (cons item ans))
188 nil))))
189 \end{elisp}
191 \begin{idea}
192 (filter '(lambda (x) (= (% x 2) 1)) '(1 2 3 4 5 6 7))
193 => (1 3 5 7)
194 \end{idea}
196 \begin{notate}{On `intersection'}
197 Set-theoretic intersection operation.
198 More general than the version coming from the `cl' package
199 \end{notate}
201 \begin{elisp}
202 (defun intersection (&rest arg)
203 (cond ((null arg) nil)
204 ((null (cdr arg)) (car arg))
205 (t (let ((ans nil))
206 (dolist (elmt (car arg) ans)
207 (let ((remainder (cdr arg)))
208 (while (and remainder
209 (member elmt (car remainder)))
210 (setq remainder (cdr remainder))
211 (when (null remainder)
212 (setq ans (cons elmt ans))))))))))
213 \end{elisp}
215 \begin{idea}
216 (intersection '(a b c d e f g h j)
217 '(a b h j k)
218 '(b d g h j k))
219 => (j h b)
220 \end{idea}
222 \begin{notate}{On `mapply'}
223 Map and apply rolled into one.
224 \end{notate}
225 \begin{elisp}
226 (defun mapply (f l)
227 (if (member nil l) nil
228 (cons (apply f (mapcar 'car l))
229 (mapply f (mapcar 'cdr l)))))
230 \end{elisp}
232 \begin{idea}
233 (mapply '+ '((1 2) (3 4)))
234 => (4 6)
235 \end{idea}
237 \begin{notate}{On `sublis'}
238 Substitute objects in a list.
239 \end{notate}
241 \begin{elisp}
242 (defun sublis (sub lis)
243 (cond
244 ((null lis) nil)
245 ((assoc lis sub) (cadr (assoc lis sub)))
246 ((atom lis) lis)
247 (t (cons (sublis sub (car lis))
248 (sublis sub (cdr lis))))))
249 \end{elisp}
251 \subsection{Core definitions}
253 \begin{notate}{The `add-plexus' function} \label{the-new-net-function}
254 We use this create a new plexus for storage. It defines a
255 counter (beginning at 1), together with several hash tables that allow
256 efficient access to the plexus' contents: an article table, forward
257 links, backward links, forward labels, and backward labels.
258 Additionally, it defines a ``ground'' and
259 ``type'' nodes.\todo{Explain these things in more detail.}\todo{NB. it could be useful
260 to maintain a registry available networks, by analogy with
261 Emacs's `buffer-list', which
262 I think could be done if we use `cl-defstruct' below
263 instead of `list', and set up the constructor suitably
264 (info \textquotedbl(cl) Structures\textquotedbl).}
265 \end{notate}
267 \begin{elisp}
268 (defun add-plexus ()
269 "Create a new plexus."
270 (let ((newbie (list '*plexus*
271 1 ; nema counter
272 (make-hash-table :test 'equal) ; nema table
273 (make-hash-table :test 'equal) ; forward links
274 (make-hash-table :test 'equal) ; backward links
275 (make-hash-table :test 'equal) ; forward labels
276 (make-hash-table :test 'equal) ; backward labels
277 (car plexus-registry))))
278 ;; Define ground and type nodes.
279 (puthash 0 '(0 0) (nth 2 newbie))
280 (puthash 1 '(0 0) (nth 2 newbie))
281 (puthash 0 '((0 . 0) (1 . 0)) (nth 3 newbie))
282 (puthash 0 '((0 . 0) (1 . 0)) (nth 4 newbie))
283 (puthash 0 '"ground" (nth 5 newbie))
284 (puthash '"ground" 0 (nth 6 newbie))
285 (puthash 1 '"type" (nth 5 newbie))
286 (puthash '"type" 1 (nth 6 newbie))
287 ;; Register the new object and return it.
288 (setq plexus-registry
289 (append
290 '(,(+ (car plexus-registry) 1)
291 (,(car plexus-registry) ,newbie))))
292 newbie))
293 \end{elisp}
295 \begin{notate}{The ``remove-plexus'' function}
296 When we're done with our plexus, we should tidy up after ourselves.
297 \end{notate}
299 \begin{elisp}
300 (defun remove-plexus (plex)
301 "Remove a plexus."
302 ;; Wipe out the hash tables
303 (dotimes (i 5)
304 (clrhash (nth (+ i 2) plex)))
305 ;; Remove the entry from the registry.
306 (setq plexus-registry
307 (cons
308 (car plexus-registry)
309 (delete
310 (assoc (nth 7 plex)
311 (cdr plexus-registry))
312 (cdr plexus-registry)))))
313 \end{elisp}
315 \begin{elisp}
316 (defun show-plexus-registry ()
317 plexus-registry)
318 \end{elisp}
320 \begin{notate}{HONEY set up}
321 These variables are needed for a coherent set-up.\todo{Explain
322 what they will do.}
323 \end{notate}
325 \begin{elisp}
326 (defvar plexus-registry (0 nil))
327 (defvar current-net nil)
328 \end{elisp}
330 \begin{notate}{Network selection}
331 We can work with several networks, only one of
332 which is ``current'' at any given time.
333 \end{notate}
335 \begin{elisp}
336 (defun set-current-plexus (plex)
337 "Examine a different plexus instead."
338 (setq current-plexus plex))
340 (defmacro with-current-plexus (plex &rest expr)
341 (append `(let ((current-plexus ,plex))) ,expr))
343 (defun show-current-plexus ()
344 "Return the plexus currently being examined."
345 current-plexus)
346 \end{elisp}
348 % \begin{notate}{On `next-unique-id'}
349 % Increment identifier that displays how many nemas are in our network.
350 % \end{notate}
352 % \begin{elisp}
353 % (defun next-unique-id ()
354 % "Produce a yet unused unique identifier."
355 % (setcar (cdr current-net)
356 % (1+ (cadr current-net))))
357 % \end{elisp}
359 \begin{notate}{On `reset-plexus'}
360 Reset article counter and hash tables. Redefine ``ground'' and
361 ``article-type''.
362 \end{notate}
364 \begin{elisp}
365 (defun reset-plexus ()
366 "Reset the database to its initial configuration."
367 ; Reset nema counter and hash tables.
368 (setcar (cdr current-plexus) 1)
369 (dotimes (n 5)
370 (clrhash (nth (+ n 2) current-plexus)))
371 ;; Define ground and nema-type.
372 (puthash 0 '(0 0) (nth 2 current-plexus))
373 (puthash 1 '(0 0) (nth 2 current-plexus))
374 (puthash 0 '((0 . 0) (1 . 0)) (nth 3 current-plexus))
375 (puthash 0 '((0 . 0) (1 . 0)) (nth 4 current-plexus))
376 (puthash 0 '"ground" (nth 5 current-plexus))
377 (puthash '"ground" 0 (nth 6 current-plexus))
378 (puthash 1 '"nema-type" (nth 5 current-plexus))
379 (puthash '"nema-type" 1 (nth 6 current-plexus))
380 nil)
381 \end{elisp}
383 \subsection{Bulk Operations}
385 \begin{notate}{On `download-en-masse'}
386 Unpack triplets, obtain labels if they exist.
387 Write data in the network to a list, and return.
388 \end{notate}
390 \begin{elisp}
391 (defun download-en-masse ()
392 "Produce a representation of the database as quintuples."
393 (let ((plex nil))
394 (maphash (lambda (uid tplt)
395 ; Unpack triplet.
396 (let ((src (car tplt))
397 (snk (nth 1 tplt))
398 (txt (nthcdr 2 tplt)))
399 ; Obtain next label if exists.
400 (setq lbl (gethash uid
401 (nth 5 current-plexus)
402 nil))
403 ; Write data to list.
404 (setq plex (cons `(,uid ,lbl ,src ,snk . ,txt)
405 plex))))
406 (nth 2 current-plexus))
407 ; Return list of data.
408 (reverse plex)))
409 \end{elisp}
411 \begin{notate}{On `upload-en-masse'}
412 Unpack quintuplets. Plug into tables.
413 Bump up article counter as needed.
414 \end{notate}
416 \begin{elisp}
418 (defun upload-en-masse (plex)
419 "Load a representation of a database as quintuples into memory."
420 (dolist (qplt plex t)
421 ; unpack quintuplet
422 (let ((uid (car qplt))
423 (lbl (nth 1 qplt))
424 (src (nth 2 qplt))
425 (snk (nth 3 qplt))
426 (txt (nthcdr 4 qplt)))
427 ; plug into tables
428 (puthash uid
429 `(,src ,snk . ,txt)
430 (nth 2 current-plexus))
431 (puthash src
432 (cons `(,uid . ,snk)
433 (gethash src (nth 3 current-plexus) nil))
434 (nth 3 current-plexus))
435 (puthash snk
436 (cons
437 `(,uid . ,src)
438 (gethash snk (nth 4 current-plexus) nil))
439 (nth 4 current-plexus))
440 (when lbl
441 (progn
442 (puthash uid lbl (nth 5 current-plexus))
443 (puthash lbl uid (nth 6 current-plexus))))
444 ; Bump up nema counter if needed.
445 (when (> uid (cadr current-plexus))
446 (setcar (cdr current-plexus) uid)))))
447 \end{elisp}
449 \begin{notate}{On `add-en-masse'}
450 Given several articles, add all of them at once.
451 \end{notate}
453 \begin{elisp}
455 (defun add-en-masse (plex)
456 "Add multiple nemata given as list of quartuplets."
457 (mapcar (lambda (qplt)
458 (Let ((uid next-unique-id))
459 (put-nema (nth 1 plex)
460 (nth 2 plex)
461 (nthcar 2 plex))
462 (label-nema uid (car qplt))))
463 plex))
464 \end{elisp}
466 \subsection{Individual Operations}
468 \begin{notate}{On `put-nema'}
469 Add record to article table.
470 Add record to list of forward links of source.
471 Add record to list of backward links of sink.
472 Return the id of the new article.\todo{Should we add an alias `add-triple'
473 for this function, to make it more clear that our middle/frontend
474 is not implementation specific?}
475 \end{notate}
477 \begin{elisp}
479 (defun add-nema (src snk txt)
480 "Enter a new nema to the database."
481 (let ((uid (next-unique-id)))
482 ;; Add record to nema table.
483 (puthash uid
484 `(,src ,snk . ,txt)
485 (nth 2 current-plexus))
486 ;; Add record to list of forward links of source.
487 (puthash src
488 (cons `(,uid . ,snk)
489 (gethash src (nth 3 current-plexus) nil))
490 (nth 3 current-plexus))
491 ;; Add record to list of backward links of sink.
492 (puthash snk
493 (cons
494 `(,uid . ,src)
495 (gethash snk (nth 4 current-plexus) nil))
496 (nth 4 current-plexus))
497 ;; Return the id of the new nema.
498 uid))
499 \end{elisp}
501 \begin{notate}{Retrieving elements of a nema}
502 These functions exist to get the relevant components
503 of a nema, given its uid.
504 \end{notate}
506 \begin{elisp}
507 (defun get-content (uid)
508 "Return the content of the nema."
509 (cddr (gethash uid (nth 2 current-plexus))))
511 (defun get-source (uid)
512 "Return the source of the nema."
513 (car (gethash uid (nth 2 current-plexus))))
515 (defun get-sink (uid)
516 "Return the sink of the nema."
517 (cadr (gethash uid (nth 2 current-plexus))))
518 \end{elisp}
520 \begin{notate}{On `update-content'}
521 old source
522 old sink
523 new content
524 \end{notate}
526 \begin{elisp}
527 (defun update-content (uid txt)
528 "Replace the content of the nema."
529 (puthash uid
530 (let ((x (gethash uid (nth 2 current-plexus))))
531 `(,(car x) ; old source
532 ,(cadr x) . ; old sink
533 ,txt)) ; new content
534 (nth 2 current-plexus)))
535 \end{elisp}
537 \begin{notate}{On `update-source'}
538 Extract current source.
539 Extract current sink.
540 Extract current content.
541 Update the entry in the article table.
542 Remove the entry with the old source in the
543 forward link table. If that is the only entry
544 filed under old-src, remove it from table.
545 Add an entry with the new source in the
546 forward link table.
547 Update the entry in the backward link table.
548 \end{notate}
550 \begin{elisp}
551 (defun update-source (uid new-src)
552 "Replace the source of the nema."
553 (let* ((x (gethash uid (nth 2 current-plexus)))
554 (old-src (car x)) ; extract current source
555 (old-snk (cadr x)) ; extract current sink
556 (old-txt (cddr x))) ; extract current content
557 ;; Update the entry in the nema table.
558 (puthash uid
559 `(,new-src ,old-snk . ,old-txt)
560 (nth 2 current-plexus))
561 ;; Remove the entry with the old source in the
562 ;; forward link table. If that is the only entry
563 ;; filed under old-src, remove it from table.
564 (let ((y (delete `(,uid . ,old-snk)
565 (gethash old-src
566 (nth 3 current-plexus)
567 nil))))
568 (if y
569 (puthash old-src y (nth 3 current-plexus))
570 (remhash old-src (nth 3 current-plexus))))
571 ;; Add an entry with the new source in the
572 ;; forward link table.
573 (puthash new-src
574 (cons `(,uid . ,old-snk)
575 (gethash old-src (nth 3 current-plexus) nil))
576 (nth 3 current-plexus))
577 ;; Update the entry in the backward link table.
578 (puthash old-snk
579 (cons `(,uid . ,new-src)
580 (delete `(,uid . ,old-src)
581 (gethash old-src
582 (nth 4 current-plexus)
583 nil)))
584 (nth 4 current-plexus))))
585 \end{elisp}
587 \begin{notate}{On `update-sink'} \label{update-sink}
588 Extract current source.
589 Extract current sink.
590 Extract current content.
591 Update the entry in the article table.
592 Remove the entry with the old sink in the
593 backward link table. If that is the only entry
594 filed under old-src, remove it from table.
595 Add an entry with the new source in the
596 backward link table.
597 Update the entry in the forward link table.
598 \end{notate}
600 \begin{elisp}
601 (defun update-sink (uid new-snk)
602 "Change the sink of the nema."
603 (let* ((x (gethash uid (nth 2 current-plexus)))
604 (old-src (car x)) ; extract current source
605 (old-snk (cadr x)) ; extract current sink
606 (old-txt (cddr x))) ; extract current content
607 ; Update the entry in the nema table.
608 (puthash uid
609 `(,old-src ,new-snk . ,old-txt)
610 (nth 2 current-plexus))
611 ;; Remove the entry with the old sink in the
612 ;; backward link table. If that is the only entry
613 ;; filed under old-src, remove it from table.
614 (let ((y (delete `(,uid . ,old-src)
615 (gethash old-snk
616 (nth 4 current-plexus)
617 nil))))
618 (if y
619 (puthash old-snk y (nth 4 current-plexus))
620 (remhash old-snk (nth 4 current-plexus))))
621 ;; Add an entry with the new source in the
622 ;; backward link table.
623 (puthash new-snk
624 (cons `(,uid . ,old-src)
625 (gethash old-snk
626 (nth 4 current-plexus)
627 nil))
628 (nth 4 current-plexus))
629 ;; Update the entry in the forward link table.
630 (puthash old-src
631 (cons `(,uid . ,new-snk)
632 (delete `(,uid . ,old-snk)
633 (gethash old-src
634 (nth 3 current-plexus)
635 nil)))
636 (nth 3 current-plexus))))
637 \end{elisp}
639 \begin{notate}{On `remove-nema'}
640 Remove forward link created by article.
641 Remove backward link created by article.
642 Remove record from article table.
643 \end{notate}
645 \begin{elisp}
646 (defun remove-nema (uid)
647 "Remove this nema from the database."
648 (let ((old-src (car (gethash uid (nth 2 current-plexus))))
649 (old-snk (cadr (gethash uid (nth 2 current-plexus)))))
650 ;; Remove forward link created by nema.
651 (let ((new-fwd (delete `(,uid . ,old-snk)
652 (gethash old-src (nth 3 current-plexus)))))
653 (if new-fwd
654 (puthash old-src new-fwd (nth 3 current-plexus))
655 (remhash old-src (nth 3 current-plexus))))
656 ;; Remove backward link created by nema.
657 (let ((new-bkw (delete `(,uid . ,old-src)
658 (gethash old-snk (nth 4 current-plexus)))))
659 (if new-bkw
660 (puthash old-snk new-bkw (nth 4 current-plexus))
661 (remhash old-snk (nth 4 current-plexus))))
662 ;; Remove record from nema table.
663 (remhash uid (nth 2 current-plexus))))
664 \end{elisp}
666 \begin{notate}{Functions for gathering links}
667 Links are stored on triples alongside other
668 elements.
669 \end{notate}
671 \begin{elisp}
672 (defun get-forward-links (uid)
673 "Return all links having given object as source."
674 (mapcar 'car (gethash uid (nth 3 current-plexus))))
676 (defun get-backward-links (uid)
677 "Return all links having given object as sink."
678 (mapcar 'car (gethash uid (nth 4 current-plexus))))
679 \end{elisp}
681 \begin{notate}{On `label-nema'}
682 Nemas can be given a unique human-readable label in addition
683 to their numeric uid.
684 \end{notate}
686 \begin{elisp}
687 (defun label-nema (uid label)
688 "Assign the label to the given object."
689 (puthash uid label (nth 5 current-plexus))
690 (puthash label uid (nth 6 current-plexus)))
691 \end{elisp}
693 \begin{notate}{Label to uid and uid to label lookup}
694 These functions allow the exchange of uid and label.
695 \end{notate}
697 \begin{elisp}
698 (defun label2uid (label)
699 "Return the unique identifier corresponding to a label."
700 (gethash label (nth 6 current-plexus) nil))
702 (defun uid2label (uid)
703 "Return the label associated to a unique identifier."
704 (gethash uid (nth 5 current-plexus) nil))
705 \end{elisp}
707 \subsection{Query}
709 \begin{notate}{Overview of search and query functionality}
710 We first describe several elementary functions for
711 accessing elements of the network. We then describe a
712 robust search pipeline and show how it is implemented.
713 \end{notate}
715 \begin{notate}{Various lookup functions}
716 These functions allow testing and lookup of various elements
717 of a net.
718 \end{notate}
720 \begin{elisp}
722 (defun uid-p (uid)
723 "Is this a valid uid?"
724 (let ((z '(())))
725 (not (eq z (gethash uid (nth 2 current-plexus) z)))))
727 (defun uid-list ()
728 "List of all valid uid's."
729 (maphash (lambda (key val) key)
730 (nth 2 current-plexus)))
732 (defun ground-p (uid)
733 "Is this nema the ground?"
734 (= uid 0))
736 (defun source-p (x y)
737 "Is the former nema the sink of the latter?"
738 (equal x (get-source y)))
740 (defun sink-p (x y)
741 "Is the former nema the sink of the latter?"
742 (equal x (get-sink y)))
744 (defun links-from (x y)
745 "Return all links from nema x to nema y."
746 (filter '(lambda (z) (source-p x z))
747 (get-backward-links y)))
749 (defun links-p (x y)
750 "Does nema x link to nema y?"
751 (when (member x (mapcar
752 'get-source
753 (get-backward-links y)))
756 (defun triple-p (x y z)
757 "Do the three items form a triplet?"
758 (and (source-p y x)
759 (sink-p y z)))
761 (defun plexus-p (x)
762 "Is this object a plexus?"
763 (let ((ans t))
764 (setq ans (and ans
765 (equal (car x) "*plexus*")))
766 (setq ans (and ans
767 (integrp (cadr x))))
768 (dotimes (n 5)
769 (setq ans (and ans (hash-table-p
770 (nth (+ n 2) x)))))
771 ans))
772 \end{elisp}
774 \section{Iteration}
776 \begin{notate}{Iterating over a plexus}
777 These functions allow users to run loops over a plexus without
778 having to delve into its internal structure.
779 \end{notate}
781 \begin{elisp}
782 (defmacro do-plexus (var res body)
783 `((maphash (lambda (,var val) ,body)
784 (nth 2 current-plexus))
785 ,res))
787 (defun map-plexus (func)
788 (let ((ans nil))
789 (maphash
790 (lambda (key val)
791 (push (apply func key) ans))
792 (nth 2 current-plexus))
793 ans))
795 (defun filter-plexus (pred)
796 (let ((ans nil))
797 (maphash
798 (lambda (key val)
799 (when (apply pred key)
800 (push key ans)))
801 (nth 2 current-plexus))
802 ans))
803 \end{elisp}
805 \begin{notate}{Additional elementary functions for node access}
806 These functions give access to the various parts of a node.\todo{Note:
807 `article-list' hasn't been defined at this point, what is it? Is it
808 something we can straightforwardly derive from `current-net'}
809 \end{notate}
811 \begin{elisp}
812 (defun get-src (n)
813 (car (nth 0 (cdr (assoc n (cdr article-list))))))
815 (defun get-flk (n)
816 (cdr (nth 0 (cdr (assoc n (cdr article-list))))))
818 (defun get-txt (n)
819 (nth 1 (cdr (assoc n (cdr article-list)))))
821 (defun get-snk (n)
822 (car (nth 2 (cdr (assoc n (cdr article-list))))))
824 (defun get-blk (n)
825 (cdr (nth 2 (cdr (assoc n (cdr article-list))))))
827 (defun get-ids nil
828 (mapcar (quote car) (cdr article-list)))
830 (defun get-gnd nil 0)
831 \end{elisp}
833 \begin{notate}{On `search-cond'} \label{search-cond}
834 Surround the search within dolist loops on free variables.
835 Wrap no further when finished.\todo{Upgrade this to concatenate the results together.
836 Also maybe allow options to add headers or to
837 only loop over unique tuplets.}\todo{Explain; how does this differ from
838 the function defined at Note \ref{search}?}
839 \end{notate}
841 \begin{elisp}
842 (defmacro search-cond (vars prop)
843 "Find all n-tuplets satisfying a condition"
844 (let ((foo '(lambda (vars cmnd)
845 (if vars
846 Wrap in a loop.
847 `(dolist (,(car vars) uids)
848 ,(funcall foo (cdr vars) cmnd))
849 cmnd))))
850 (funcall foo vars prop)))
851 \end{elisp}
853 \begin{notate}{Overview of the search pipeline}
854 We will implement the search as a pipeline which gradually
855 transforms the query into a series of expressions which produce
856 the sought-after result, then evaluate those expressions.
858 A search query designates predicates apply to the nodes
859 and the network relationships that apply to them. The network relationships
860 function as wildcards.
862 The basic model of the data is triplets that point to other triplets.
863 The following query asks for a \emph{funny} link from a
864 \emph{big blue object} to a \emph{small green link} pointing outwards
865 from the big blue object.
866 \begin{idea}
867 (((a blue big) (b funny) (c green small)
868 ((b src a) (b snk c) (c src a))
869 \end{idea}
870 The first step of processing is to put the quaerenda in some
871 order so that each item links up with at least one previous item:
872 \begin{idea}
873 (scheduler
874 '((b funny)
875 (c green small))
876 '((b src a)
877 (b snk c)
878 (c src a))
879 '((a blue big)))
881 ((c (green small) ((b snk c) (c src a)))
882 (b (funny) ((b src a)))
883 (a blue big))
884 \end{idea}
885 Note that the order is reversed due to technicalities of
886 implementing `scheduler' --- that is to say, a is first and does
887 not link to any other variable, b is next and links to only a,
888 whilst c is last and links to both a and b.
889 At the same time, we have also rearranged things so that the
890 links to previous items to which a given object are listed
891 alongside that object. The next step is to replace the links with the commands which
892 generate a list of such objects:
893 \begin{idea}
894 ((c (green small) ((b snk c) (c src a)))
895 (b (funny) ((b src a)))
896 (a blue big))
898 ((c (green small)
899 (intersection (list (get-snk b)) (get-forward-links a)))
900 (b (funny)
901 (intersection (get-backward-links a)))
902 (a blue big))
903 \end{idea}
904 This is done using the function `tplts2cmd', e.g.
905 \begin{idea}
906 (tplts2cmd 'c '((b snk c) (c src a)))
908 (intersection (list (get-snk b)) (get-forward-links a))
909 \end{idea}
910 Subsequently, we filter over the predicates:
911 \begin{idea}
912 ((c (filter '(lambda (c) (and (green c) (small c)))
913 (intersection (list (get-snk b))
914 (get-forward-links))))
915 (b (filter '(lambda (b) (and (funny b)))
916 (intersection (get-backward-links a)))))
917 \end{idea}
918 This is done with the command `add-filt':
919 \begin{idea}
920 (add-filt 'c
921 '(green small)
922 '((b snk c) (c src a)))
924 (c (filter (quote (lambda (c) (and (green c) (small c))))
925 (intersection (list (get-snk b))
926 (get-forward-links a))))
927 \end{idea}
928 This routine calls up the previously described routine `tplts2cmd'
929 to take care of the third argument. The last entry, {\tt (a blue big)}
930 gets processed a little differently because we don't as yet have
931 anything to filter over; instead, we generate the initial list by
932 looping over the current network:
933 \begin{idea}
934 (a (let ((ans nil))
935 (donet 'node
936 (when (and (blue (get-content node))
937 (big (get-content node)))
938 (setq ans (cons node ans))))
939 ans))
940 \end{idea}
941 This is done by invoking `first2cmd':
942 \begin{idea}
943 (first2cmd '(blue big))
945 (let ((ans nil))
946 (donet (quote node)
947 (when (and (blue (get-content node))
948 (big (get-content node)))
949 (setq ans (cons node ans))))
950 ans)
951 \end{idea}
952 And putting this all together:
953 \begin{idea}
954 (query2cmd
955 '((c (green small) ((b snk c) (c src a)))
956 (b (funny) ((b src a)))
957 (a blue big)))
959 ((c (filter (quote (lambda (c) (and (green c) (small c))))
960 (intersection (list (get-snk b))
961 (get-forward-links a))))
962 (b (filter (quote (lambda (b) (and (funny b))))
963 (intersection (get-forward-links a))))
964 (a (let ((ans nil))
965 (donet (quote node)
966 (when (and (blue (get-content node))
967 (big (get-content node)))
968 (setq ans (cons node ans))))
969 ans)))
970 \end{idea}
971 To carry out these instructions in the correct order and generate
972 a set of variable assignments, we employ the `matcher' function.
973 Combining this last layer, we have the complete pipeline:
974 \begin{idea}
975 (matcher nil
976 (query2cmd
977 (scheduler
978 '((b funny)
979 (c green small))
980 '((b src a)
981 (b snk c)
982 (c src a))
983 '((a blue big)))))
984 \end{idea}
985 This combination of operations is combined into the `search'
986 function, which can be called as follows:
987 \begin{idea}
988 (search
989 '(((a blue big)
990 (b funny)
991 (c green small))
992 ((b src a)
993 (b snk c)
994 (c src a))))
995 \end{idea}
997 Having described what the functions are supposed to do and how
998 they work together, we now proceed to implement them.
999 \end{notate}
1001 \begin{notate}{On `scheduler'}
1002 The scheduler function takes a list search query and rearranges it
1003 into an order suitable for computing the answer to that query.
1004 Specifically, a search query is a pair of lists --- the first list
1005 consists of lists whose heads are names of variables and whose
1006 tails are predicates which the values of the variables should
1007 satisfy and the second list consists of triples indicating the
1008 relations between the values of the variables.
1009 Its arguments are:
1010 \begin{itemize}
1011 \item new-nodes, a list of items of the form \verb|(node &rest property)|;
1012 \item \verb|links|, a list of triplets;
1013 \item \verb|sched| is a list whose items consist of triplets of the
1014 form\newline \verb|(node (&rest property) (&rest link))|.
1015 \end{itemize}
1017 A recursive function to find linked nodes.
1018 If done, return answer.
1019 New nodes yet to be examined.
1020 Element of remaining-nodes currently under consideration.
1021 List of links between candidate and old-nodes.
1022 List of nodes already scheduled.
1023 Loop through new nodes until find one linked to an old node.
1024 Look at the next possible node.
1025 Find the old nodes linking to the candidate node and record the answer in ``ties''.
1026 Pick out the triplets whose first element is the node under consideration and whose third element is already on the list or vice-versa.
1027 Recursively add the rest of the nodes.
1028 \end{notate}
1030 \begin{elisp}
1031 (defun scheduler (new-nodes links sched)
1032 (if (null new-nodes)
1033 sched
1034 (let ((remaining-nodes new-nodes)
1035 (candidate nil)
1036 (ties nil)
1037 (old-nodes (mapcar 'car sched)))
1038 (while (null ties)
1039 (setq candidate (car remaining-nodes))
1040 (setq remaining-nodes (cdr remaining-nodes))
1041 (setq ties
1042 (filter '(lambda (x)
1044 (and (eq (first x) (car candidate))
1045 (member (third x) old-nodes))
1046 (and (member (first x) old-nodes)
1047 (eq (third x) (car candidate)))))
1048 links)))
1049 (scheduler (remove candidate new-nodes)
1050 links
1051 (cons (list (car candidate)
1052 (cdr candidate)
1053 ties)
1054 sched)))))
1055 \end{elisp}
1057 \begin{notate}{On `tplts2cmd'}
1058 \ldots\todo{Explain.}
1059 \end{notate}
1061 \begin{elisp}
1062 (defun tplts2cmd (var tplts)
1063 (cons 'intersection
1064 (mapcar
1065 '(lambda (tplt)
1066 (cond ((and (eq (third tplt) var)
1067 (eq (second tplt) 'src))
1068 `(get-flk ,(first tplt)))
1069 ((and (eq (third tplt) var)
1070 (eq (second tplt) 'snk))
1071 `(get-blk ,(first tplt)))
1072 ((and (eq (first tplt) var)
1073 (eq (second tplt) 'src))
1074 `(list (get-src ,(third tplt))))
1075 ((and (eq (first tplt) var)
1076 (eq (second tplt) 'snk))
1077 `(list (get-snk ,(third tplt))))
1078 (t nil)))
1079 tplts)))
1080 \end{elisp}
1082 \begin{notate}{On `add-filt'}
1083 \ldots\todo{Explain.}
1084 \end{notate}
1086 \begin{elisp}
1087 (defun add-filt (var preds tplts)
1088 `(,var
1089 (filter
1090 '(lambda (,var)
1091 ,(cons 'and
1092 (mapcar
1093 '(lambda (pred)
1094 (list pred
1095 (list 'get-txt var)))
1096 preds)))
1097 ,(tplts2cmd var tplts))))
1098 \end{elisp}
1100 \begin{notate}{On `first2cmd'}
1101 \ldots\todo{Explain.}
1102 \end{notate}
1104 \begin{elisp}
1105 (defun first2cmd (preds)
1106 `(let ((ans nil))
1107 (dolist (node (get-ids) ans)
1108 (when
1109 ,(cons 'and
1110 (mapcar
1111 '(lambda (pred)
1112 (cons pred '((get-txt node))))
1113 preds))
1114 (setq ans (cons node ans))))))
1115 \end{elisp}
1117 \begin{notate}{On `query2cmd'}
1118 \ldots\todo{Explain.}
1119 \end{notate}
1121 \begin{elisp}
1122 (defun query2cmd (query)
1123 (let ((backwards (reverse query)))
1124 (reverse
1125 (cons
1126 (list (caar backwards)
1127 (first2cmd (cdar backwards)))
1128 (mapcar
1129 '(lambda (x)
1130 (add-filt (first x) (second x) (third x)))
1131 (cdr backwards))))))
1132 \end{elisp}
1134 \begin{notate}{On `matcher'}
1135 \ldots\todo{Explain.}
1136 \end{notate}
1138 \begin{elisp}
1139 (defun matcher (assgmt reqmts)
1140 (if (null reqmts) (list assgmt)
1141 (apply 'append
1142 (mapcar
1143 '(lambda (x)
1144 (matcher (cons (list (caar reqmts) x)
1145 assgmt)
1146 (cdr reqmts)))
1147 (apply 'intersection
1148 (eval `(let ,assgmt
1149 (mapcar 'eval
1150 (cdar reqmts)))))))))
1151 \end{elisp}
1153 \begin{notate}{How matcher works}
1154 Here are some examples unrelated to what comes up in searching
1155 triplets which illustrate how matcher works:
1156 \end{notate}
1158 \begin{idea}
1159 (matcher '((x 1)) '((y (list 1 3))
1160 (z (list (+ x y) (- y x)))))
1162 (((z 2) (y 1) (x 1))
1163 ((z 0) (y 1) (x 1))
1164 ((z 4) (y 3) (x 1))
1165 ((z 2) (y 3) (x 1)))
1167 (matcher nil '((x (list 1))
1168 (y (list 1 3))
1169 (z (list (+ x y) (- y x)))))
1171 (((z 2) (y 1) (x 1))
1172 ((z 0) (y 1) (x 1))
1173 ((z 4) (y 3) (x 1))
1174 ((z 2) (y 3) (x 1)))
1175 \end{idea}
1177 \begin{notate}{On `search'} \label{search}
1178 \ldots\todo{Explain; how does this differ from
1179 the macro defined at Note \ref{search-cond}?}
1180 \end{notate}
1182 \begin{elisp}
1183 (defun search (query)
1184 (matcher nil
1185 (reverse
1186 (query2cmd
1187 (scheduler
1188 (cdar query)
1189 (cadr query)
1190 (list (caar query)))))))
1191 \end{elisp}
1193 \begin{notate}{Search convenience functions}
1194 Several convenience functions for search can be
1195 defined.\todo{The code here is just a sketch, but hopefully something similar will actually work!}
1196 \end{notate}
1198 \begin{elisp}
1199 (defun triples-given-beginning (node)
1200 "Get triples outbound from the given NODE."
1201 (search `(((a ,node))
1202 ((c src a)
1203 (c snk b)))))
1205 (defun triples-given-end (node)
1206 "Get triples inbound into NODE."
1207 (search `(((b ,node))
1208 ((c src a)
1209 (c snk b)))))
1211 (defun triples-given-middle (edge)
1212 "Get the triples that run along EDGE."
1213 (search `(((c ,edge))
1214 ((c src a)
1215 (c snk b)))))
1217 (defun triples-given-middle-and-end (edge node)
1218 "Get the triples that run along EDGE into NODE."
1219 (search `(((c ,edge) (b ,node))
1220 ((c src a)
1221 (c snk b)))))
1223 (defun triples-given-beginning-and-middle (node edge)
1224 "Get the triples that run from NODE along EDGE."
1225 (search `(((a ,node) (c ,edge))
1226 ((c src a)
1227 (c snk b)))))
1229 (defun triples-given-beginning-and-end (node1 node2)
1230 "Get the triples that run from NODE1 to NODE2. Optional
1231 argument VIEW causes the results to be selected
1232 into a view with that name."
1233 (search `(((a ,node1) (b ,node2))
1234 ((c src a)
1235 (c snk b)))))
1237 (defun triple-exact-match (node1 edge node2)
1238 "Get the triples that run from NODE1 along EDGE to
1239 NODE2."
1240 (search `(((a ,node1) (b ,node2) (c ,edge))
1241 ((c src a)
1242 (c snk b)))))
1243 \end{elisp}
1245 \subsection{Scholium programming}
1247 \begin{notate}{Scholium programming}
1248 The next several functions allow us to store and retrieve code
1249 from inside of the network.
1250 \end{notate}
1252 \begin{notate}{On `node-fun'}
1253 \ldots\todo{Explain.}
1254 Produce a list of commands to produce temporary bindings.
1255 Produce a list of commands to reset function values.
1256 \end{notate}
1258 \begin{elisp}
1259 (defun node-fun (node get-code get-links)
1260 (let ((code (funcall get-code node))
1261 (links (funcall get-links node)))
1262 (list
1263 'lambda
1264 (car code)
1265 (cons
1266 'prog1
1267 (cons
1268 (append
1269 '(progn)
1270 (mapcar '#(lambda (x)
1271 `(fset ',(car x)
1272 (node-fun ,(cdr x)
1273 ',get-code
1274 ',get-links)))
1275 links)
1276 (cdr code))
1277 (mapcar '#(lambda (x)
1278 (if (fboundp (car x))
1279 `(fset ',(car x)
1280 ',(symbol-function (car x)))
1281 `(fmakunbound ',(car x))))
1282 links))))))
1283 \end{elisp}
1285 \begin{notate}{On `tangle-module'}
1286 Recursively replace the chunks to recover executable code.\todo{Explain.}
1287 \end{notate}
1289 \begin{elisp}
1290 (defun tangle-module (node get-cont ins-links)
1291 (insert-chunk
1292 (funcall get-cont node)
1293 (mapcar '#(lambda (x)
1294 (cons (car x)
1295 (tangle-module (cdr x)
1296 get-cont
1297 ins-links)))
1298 (funcall ins-links node))))
1299 \end{elisp}
1301 \begin{notate}{On `insert-chunk'}
1302 Given a node and an association list of replacement texts, insert
1303 the chunks at the appropriate places.
1304 \end{notate}
1306 \begin{elisp}
1307 (defun insert-chunk (body chunks)
1308 (cond ((null body) nil)
1309 ((null chunks) body)
1310 ((equal (car body) '*insert*)
1311 (cdr (assoc (cadr body) chunks)))
1312 (t (cons (insert-chunk (car body) chunks)
1313 (insert-chunk (cdr body) chunks)))))
1314 \end{elisp}
1316 \begin{notate}{Functions for rewriting nemas}
1317 Several functions for rewriting nemas.\todo{How does this stuff relate to what's
1318 going on in the vicinity of Note \ref{update-sink}?}
1319 \end{notate}
1321 \begin{elisp}
1322 (defun set-src (n x)
1323 (if (equal n 0)
1325 (progn (let ((old-backlink
1326 (nth 1 (assoc (get-src n)
1327 (cdr article-list)))))
1328 (setcdr old-backlink
1329 (delete n (cdr old-backlink))))
1330 (let ((new-backlink
1331 `(nth 1 (assoc x (cdr article-list)))))
1332 (setcdr new-backlink (cons n (cdr new-backlink))))
1333 (setcar (nth 1 (assoc n (cdr article-list))) x))))
1335 (defun set-txt (n x)
1336 (setcar (cdr (cdr (assoc n (cdr article-list)))) x))
1338 (defun set-snk (n x)
1339 (if (equal n 0)
1341 (progn (let ((old-backlink
1342 (nth 3 (assoc (get-snk n)
1343 (cdr article-list)))))
1344 (setcdr old-backlink
1345 (delete n (cdr old-backlink))))
1346 (let ((new-backlink
1347 (nth 3 (assoc x (cdr article-list)))))
1348 (setcdr new-backlink (cons n (cdr new-backlink))))
1349 (setcar (nth 3 (assoc n (cdr article-list))) x))))
1351 (defun ins-nod (src txt snk)
1352 (progn (setcdr article-list
1353 (cons (list (car article-list)
1354 (list src)
1356 (list snk))
1357 (cdr article-list)))
1358 (let ((backlink
1359 (nth 3 (assoc snk (cdr article-list)))))
1360 (setcdr backlink (cons (car article-list)
1361 (cdr backlink))))
1362 (let ((backlink
1363 (nth 1 (assoc src (cdr article-list)))))
1364 (setcdr backlink (cons (car article-list)
1365 (cdr backlink))))
1366 (- (setcar article-list (+ 1 (car article-list))) 1)))
1368 (defun del-nod (n)
1369 (if (or (equal n 0)
1370 (get-blk n)
1371 (get-flk n))
1373 (progn (let ((old-backlink
1374 (nth 3 (assoc (get-snk n)
1375 (cdr article-list)))))
1376 (setcdr old-backlink
1377 (delete n (cdr old-backlink))))
1378 (let ((old-backlink
1379 (nth 1 (assoc (get-src n)
1380 (cdr article-list)))))
1381 (setcdr old-backlink
1382 (delete n (cdr old-backlink))))
1383 (setcdr article-list
1384 (delete (assoc n (cdr article-list))
1385 (cdr article-list)))
1386 t)))
1387 \end{elisp}
1389 \subsection{Initialization}
1391 \begin{notate}{Initialize with a new network}
1392 For now, we just create one network to import things into. Additional
1393 networks can be added later (see Section \ref{applications}).
1394 \end{notate}
1396 \begin{elisp}
1397 (set-net (new-net root-level))
1398 \end{elisp}
1400 \section{An example middle\"end} \label{middle-end}
1402 \begin{notate}{A middle\"end for managing collections of articles}
1403 This middle\"end is a set of functions that add triples into the
1404 backend. At this stage we basically ignore details of storage, and
1405 rely on the convenience functions defined above as the backend's API.
1406 In principle it would be possible to swap out the backend for another
1407 storage mechanism. We will give an example later on that uses more of
1408 the LISP-specific aspects of the backend implementation.\todo{Let's
1409 try to be a bit more concrete about this, especially in Section
1410 \ref{farm-demo}.} In this example, rather than talking about nemas
1411 and networks, we will talk about \emph{articles} and \emph{scholia}.
1412 These objects are things that user will want to access, create, and
1413 manipulate. However, we will deal with functions for user interaction
1414 (input, display, and editing) in Section \ref{frontend}, not here.
1415 Like the backend, the middle\"end could also be swapped out in
1416 applications where a different kind of data is modelled. And in fact,
1417 we come to some examples of other mid-level interfaces in Section
1418 \ref{applications}.
1419 \end{notate}
1421 \subsection{Database interaction} \label{interaction}
1423 \begin{notate}{The `article' function} \label{the-article-function}
1424 You can use this function to create an article with a
1425 given name and contents. You can optionally put it in a
1426 list by specifying the heading that it is under.
1427 \end{notate}
1429 \begin{elisp}
1430 (defun article (name contents &optional heading)
1431 (let ((coordinates (put-nema name
1432 "has content"
1433 contents)))
1434 (when heading (put-nema coordinates "in" heading))
1435 coordinates))
1436 \end{elisp}
1438 \begin{notate}{The `scholium' function} \label{the-scholium-function}
1439 You can use this function to link annotations to objects.
1440 As with the `article' function, you can optionally
1441 categorize the connection on a given list (cf. Note
1442 \ref{the-article-function}).
1443 \end{notate}
1445 \begin{elisp}
1446 (defun scholium (beginning link end &optional heading)
1447 (let ((coordinates (put-nema beginning
1448 link
1449 end)))
1450 (when heading (put-nema coordinates "in" heading))
1451 coordinates))
1452 \end{elisp}
1454 \begin{notate}{Uses of coordinates}
1455 It is convenient to do further immediate processing of the object
1456 we've created while we still have ahold of the coordinates
1457 returned by `put-nema' (e.g., for importing code
1458 that is adjacent to the article, see Note
1459 \ref{import-code-continuations}).
1460 \end{notate}
1462 \begin{notate}{On `get-article'} \label{get-article}
1463 Get the contents of the article named `name'.
1464 We assume that there is only one such article for now.
1465 \end{notate}
1467 \begin{elisp}
1468 ;; Something like this.
1469 (defun get-article (name)
1470 (third (triples-given-beginning-and-middle
1471 name "has content")))
1472 \end{elisp}
1474 \begin{notate}{On `get-names'} \label{get-names}
1475 This function simply gets the names of articles that have
1476 names -- in other words, every triple built around the
1477 ``has content'' relation.
1478 \end{notate}
1480 \begin{elisp}
1481 ;; Something like this.
1482 (defun get-names (&optional heading)
1483 (map first (triples-given-middle "has content")))
1484 \end{elisp}
1486 \section{An example frontend} \label{frontend}
1488 \begin{notate}{Overview of the frontend}
1489 The frontend provides a demonstration of Arxana's functionality that
1490 is directly accessible to the user. Specifically, it is used to
1491 import \LaTeX\ documents into a network structure. They can then be
1492 edited, remixed, saved, browsed, and exported.\todo{Some of this
1493 functionality still needs to be merged in or written!}
1494 \end{notate}
1496 \subsection{Importing \LaTeX\ documents} \label{importing}
1498 \begin{notate}{Importing sketch} \label{importing-sketch}
1499 The code in this section imports a document, represented as a
1500 collection of (sub-)sections and notes. It gathers the sections,
1501 sub-sections, and notes recursively and records their content in a
1502 tree whose nodes are places and whose links express the
1503 ``component-of'' relation described in Note \ref{order-of-order}.
1505 This representation lets us see the geometric, hierarchical, structure
1506 of the document we've imported. It exemplifies a general principle,
1507 that geometric data should be represented by relationships between
1508 places, not direct relationships between strings. This is because
1509 ``the same'' string often appears in ``different'' places in any given
1510 document (e.g. a paper's many sub-sections titled ``Introduction''
1511 will not all have the same content).\todo{Do we need to relax this?}
1513 What goes into the places is in some sense arbitrary. The key is that
1514 whatever is in or attached to these places must tell us
1515 everything we need to know about the part of the document associated
1516 with that place (e.g., in the case of a note, its title and contents).
1517 That's over and above the structural links which say how the
1518 places relate to one another. Finally, all of these places and
1519 structural links will be added to a heading that represents the
1520 document as a whole.
1522 A natural convention we'll use is to put the name of any document
1523 component that's associated with a given place into that place, and
1524 add all other information as annotations.\todo{Does this contradict
1525 what is said above about Introductions?}
1527 Following our usual coding convention, functions are introduced
1528 below ``from the bottom up.''
1529 \end{notate}
1531 \begin{notate}{On `import-code-continuations'} \label{import-code-continuations}
1532 This function runs within the scope of `import-notes'.
1533 In fact, it is meant to run right after a Note itself
1534 has been scanned. The job of this function is to turn the
1535 series of Lisp chunks or other code snippets that follow a given note
1536 into a scholium attached to that note. Each separate snippet becomes
1537 its own annotation. The ``conditional regexps'' used here only work
1538 with Emacs version 23 or higher.\todo{I'm noticing a problem with the
1539 way the `looking-at' form behaves. It matches the expression in
1540 question, but then the match-end is reported as one character less
1541 than it supposed to be. Maybe `looking-at' is just not as good as
1542 `re-search-forward'? But it's what seems easiest to use.}
1543 \end{notate}
1545 \begin{elisp}
1546 ;; coords don't exist anymore, now we use uids
1547 (defun import-code-continuations (coords)
1548 (let ((possible-environments
1549 "\\(1?:lisp\\|idea\\|common\\)"))
1550 (while (looking-at
1551 (concat "\n*?\\\\begin{"
1552 possible-environments
1553 "}"))
1554 (let* ((beg (match-end 0))
1555 (environment (match-string 1))
1556 (end (progn (search-forward-regexp
1557 (concat "\\\\end{"
1558 environment
1559 "}"))
1560 (match-beginning 0)))
1561 (content (buffer-substring-no-properties
1563 end)))
1564 (scholium (scholium coords
1565 "has attachment"
1566 content)
1567 "has type"
1568 environment)))))
1569 \end{elisp}
1571 \begin{notate}{On `import-notes'} \label{import-notes}
1572 We're going to make the daring assumption that the ``textual''
1573 portions of incoming \LaTeX\ documents are contained in ``Notes''.
1574 That assumption is true, at least, for the current document. The
1575 function takes a buffer position `end' that denotes the end of the
1576 current section. The function returns the count of the number of
1577 notes imported, so that `import-within' knows where to start counting
1578 this section's non-note children.\todo{Would this same function work
1579 to import all notes from a buffer without examining its sectioning
1580 structure? Not quite, but close! (Could be a fun exercise to fix
1581 this.)}
1582 \end{notate}
1584 \begin{elisp}
1585 (defun import-notes (end)
1586 (let ((index 0))
1587 (while (re-search-forward (concat "\\\\begin{notate}"
1588 "{\\([^}\n]*\\)}"
1589 "\\( +\\\\label{\\)?"
1590 "\\([^}\n]*\\)?")
1591 end t)
1592 (let* ((name
1593 (match-string-no-properties 1))
1594 (tag (match-string-no-properties 3))
1595 (beg
1596 (progn (next-line 1)
1597 (line-beginning-position)))
1598 (end
1599 (progn (search-forward-regexp
1600 "\\\\end{notate}")
1601 (match-beginning 0)))
1602 ;; this has to change
1603 (coords (place-item name nil buffername)))
1604 (setq index (1+ index))
1605 (scholium current-parent
1606 index
1607 coords
1608 buffername)
1609 (scholium coords
1610 "has content"
1611 (buffer-substring-no-properties
1612 beg end))
1613 (import-code-continuations coords)))
1614 index))
1615 \end{elisp}
1617 \begin{notate}{On `import-within'}
1618 Recurse through levels of sectioning in a document to import
1619 \LaTeX\ code. Children that are notes are attached to the
1620 hierarchical structure by the subroutine `import-notes', called by
1621 this function. Sections are attached directly by `import-within'. We
1622 observe that a note ``has content'' whereas a section does not.
1624 Incidentally, when looking for the end of an importing level, `nil' is
1625 an OK result: that describes the case when we have reached the
1626 \emph{last} section at this level \emph{and} there is no subsequent
1627 section at a higher level.
1628 \end{notate}
1630 \begin{elisp}
1631 (defun import-within (levels)
1632 (let ((this-level (car levels))
1633 (next-level (car (cdr levels))) answer)
1634 (while (re-search-forward
1635 (concat
1636 "^\\\\" this-level "{\\([^}\n]*\\)}"
1637 "\\( +\\\\label{\\)?"
1638 "\\([^}\n]*\\)?")
1639 level-end t)
1640 (let* ((name (match-string-no-properties 1))
1641 (at (place-item name nil buffername))
1642 (level-end
1643 (or (save-excursion
1644 (search-forward-regexp
1645 (concat "^\\\\" this-level "{.*")
1646 level-end t))
1647 level-end))
1648 (notes-end
1649 (if next-level
1650 (or (progn (point)
1651 (save-excursion
1652 (search-forward-regexp
1653 (concat "^\\\\"
1654 next-level "{.*")
1655 level-end t)))
1656 level-end)
1657 level-end))
1658 (index (let ((current-parent at))
1659 (import-notes notes-end)))
1660 (subsections (let ((current-parent at))
1661 (import-within (cdr levels)))))
1662 (while subsections
1663 (let ((coords (car subsections)))
1664 (setq index (1+ index))
1665 (scholium at
1666 index
1667 coords
1668 buffername)
1669 (setq subsections (cdr subsections))))
1670 (setq answer (cons at answer))))
1671 (reverse answer)))
1672 \end{elisp}
1674 \begin{notate}{On `import-buffer'}
1675 This function import a \LaTeX\ document, taking care of
1676 the high-level, non-recursive, aspects of this operation.
1677 It imports frontmatter (everything up to the first
1678 \verb+\begin{section}+), but assumes ``backmatter'' is
1679 trivial, and does not attempt to import it. The imported
1680 material is classified as a ``document'' with the same
1681 name as the imported buffer.
1683 Links to sections will be made under the ``heading'' of this
1684 document.\todo{The sectioning levels should maybe be scholia attached
1685 to root-coords, but for some reason that wasn't working so well --
1686 investigate later -- maybe it just wasn't good to run after running
1687 `import-within'.}
1688 \end{notate}
1690 \begin{elisp}
1691 (defun import-buffer (&optional buffername)
1692 (save-excursion
1693 (set-buffer (get-buffer (or buffername
1694 (current-buffer))))
1695 (goto-char (point-min))
1696 (search-forward-regexp "\\\\begin{document}")
1697 (search-forward-regexp "\\\\section")
1698 (goto-char (match-beginning 0))
1699 (scholium buffername "is a" "document")
1700 (scholium buffername
1701 "has frontmatter"
1702 (buffer-substring-no-properties
1703 (point-min)
1704 (point))
1705 buffername)
1706 (let* ((root-coords (place-item buffername nil
1707 buffername))
1708 (levels
1709 '("section" "subsection" "subsubsection"))
1710 (current-parent buffername)
1711 (level-end nil)
1712 (sections (import-within levels))
1713 (index 0))
1714 (while sections
1715 (let ((coords (car sections)))
1716 (setq index (1+ index))
1717 (scholium root-coords
1718 index
1719 coords
1720 buffername))
1721 (setq sections (cdr sections))))))
1722 \end{elisp}
1724 \begin{notate}{On `autoimport-arxana'} \label{autoimport-arxana}
1725 This just calls `import-buffer', and imports this document
1726 into the system.
1727 \end{notate}
1729 \begin{elisp}
1730 (defun autoimport-arxana ()
1731 (interactive)
1732 (import-buffer "arxana.tex"))
1733 \end{elisp}
1735 \subsection{Browsing database contents} \label{browsing}
1737 \begin{notate}{Browsing sketch} \label{browsing-sketch}
1738 This section facilitates browsing of documents represented
1739 with structures like those created in Section
1740 \ref{importing}, and sets the ground for browsing other
1741 sorts of contents (e.g. collections of tasks, as in
1742 Section \ref{managing-tasks}).
1744 In order to facilitate general browsing, it is not enough
1745 to simply use `get-article' (Note \ref{get-article}) and
1746 `get-names' (Note \ref{get-names}), although these
1747 functions provide our defaults. We must provide the means
1748 to find and display different things differently -- for
1749 example, a section's table of contents will typically
1750 be displayed differently from its actual contents.
1752 Indeed, the ability to display and select elements of
1753 document sections (Note \ref{display-section}) is
1754 basically the core browsing deliverable. In the process
1755 we develop a re-usable article selector (Note
1756 \ref{selector}; cf. Note \ref{browsing-tasks}). This in
1757 turn relies on a flexible function for displaying
1758 different kinds of articles (Note \ref{display-article}).
1759 \end{notate}
1761 \begin{notate}{On `display-article'} \label{display-article}
1762 This function takes in the name of the article to display.
1763 Furthermore, it takes optional arguments `retriever' and
1764 `formatter', which tell it how to look up and/or format
1765 the information for display, respectively.
1767 Thus, either we make some statement up front (choosing our
1768 `formatter' based on what we already know about the
1769 article), or we decide what to display after making some
1770 investigation of information attached to the article, some
1771 of which may be retrieved and displayed (this requires
1772 that we specify a suitable `retriever' and a complementary
1773 `formatter').
1775 For example, the major mode in which to display the
1776 article's contents could be stored as a scholium attached
1777 to the article; or we might maintain some information
1778 about ``areas'' of the database that would tell us up
1779 front what which mode is associated with the current area.
1780 (The default is to simply insert the data with no markup
1781 whatsoever.)
1783 Observe that this works when no heading argument is given,
1784 because in that case `get-article' looks for \emph{all}
1785 place pseudonyms. (But of course that won't work well
1786 when we have multiple theories containing things with the
1787 same names, so we should get used to using the heading
1788 argument.)
1790 (The business about requiring the data to be a sequence
1791 before engaging in further formatting is, of course, just
1792 a matter of expediency for making things work with the
1793 current dataset.)
1794 \end{notate}
1796 \begin{elisp}
1797 (defun display-article
1798 (name &optional heading retriever formatter)
1799 (interactive "Mname: ")
1800 (let* ((data (if retriever
1801 (funcall retriever name heading)
1802 (get-article name heading))))
1803 (when (and data (sequencep data))
1804 (save-excursion
1805 (if formatter
1806 (funcall formatter data heading)
1807 (pop-to-buffer (get-buffer-create
1808 "*Arxana Display*"))
1809 (delete-region (point-min) (point-max))
1810 (insert "NAME: " name "\n\n")
1811 (insert data)
1812 (goto-char (point-min)))))))
1813 \end{elisp}
1815 \begin{notate}{An interactive article selector} \label{selector}
1816 The function `get-names' (Note \ref{get-names}) and
1817 similar functions can give us a collection of articles.
1818 The next few functions provide an interactive
1819 functionality for moving through this collection to find
1820 the article we want to look at.
1822 We define a ``display style'' that the article selector
1823 uses to determine how to display various articles. These
1824 display styles are specified by text properties attached
1825 to each option the selector provides. Similarly, when
1826 we're working within a given heading, the relevant heading
1827 is also specified as a text property.
1829 At selection time, these text properties are checked to
1830 determine which information to pass along to
1831 `display-article'.
1832 \end{notate}
1834 \begin{elisp}
1835 (defvar display-style '((nil . (nil nil))))
1837 (defun thing-name-at-point ()
1838 (buffer-substring-no-properties
1839 (line-beginning-position)
1840 (line-end-position)))
1842 (defun get-display-type ()
1843 (get-text-property (line-beginning-position)
1844 'arxana-display-type))
1846 (defun get-relevant-heading ()
1847 (get-text-property (line-beginning-position)
1848 'arxana-relevant-heading))
1850 (defun arxana-list-select ()
1851 (interactive)
1852 (apply 'display-article
1853 (thing-name-at-point)
1854 (get-relevant-heading)
1855 (cdr (assoc (get-display-type)
1856 display-style))))
1858 (define-derived-mode arxana-list-mode fundamental-mode
1859 "arxana-list" "Arxana List Mode.
1861 \\{arxana-list-mode-map}")
1863 (define-key arxana-list-mode-map (kbd "RET")
1864 'arxana-list-select)
1865 \end{elisp}
1867 \begin{notate}{On `pick-a-name'} \label{pick-a-name}
1868 Here `generate' is the name of a function to call to
1869 generate a list of items to display, and `format' is a
1870 function to put these items (including any mark-up) into
1871 the buffer from which individiual items can then be
1872 selected.
1874 One simple way to get a list of names to display would be
1875 to reuse a list that we had already produced (this would
1876 save querying the database each time). We could, in fact,
1877 store a history list of lists of names that had been
1878 displayed previously (cf. Note \ref{local-storage}).
1880 We'll eventually want versions of `generate' that provide
1881 various useful views into the data, e.g., listing all of
1882 the elements of a given section (Note
1883 \ref{display-section}).
1885 Finding all the elements that match a given search term,
1886 whether that's just normal text search or some kind of
1887 structured search would be worthwhile too. Upgrading the
1888 display to e.g. color-code listed elements according to
1889 their type would be another nice feature to add.
1890 \end{notate}
1892 \begin{elisp}
1893 (defun pick-a-name (&optional generate format heading)
1894 (interactive)
1895 (let ((items (if generate
1896 (funcall generate)
1897 (get-names heading))))
1898 (when items
1899 (set-buffer (get-buffer-create "*Arxana Articles*"))
1900 (toggle-read-only -1)
1901 (delete-region (point-min)
1902 (point-max))
1903 (if format
1904 (funcall format items)
1905 (mapc (lambda (item) (insert item "\n")) items))
1906 (toggle-read-only t)
1907 (arxana-list-mode)
1908 (goto-char (point-min))
1909 (pop-to-buffer (get-buffer "*Arxana Articles*")))))
1910 \end{elisp}
1912 \begin{notate}{On `get-section-contents'} \label{get-section-contents}
1913 This function is used by `display-section'
1914 (Note \ref{display-section}) to `pick-a-name' as a generator
1915 for the table of contents of the section with the given
1916 name under the given heading.
1918 This function first finds the triples that begin with the
1919 (placed) name of the section, then checks to see which of
1920 these are in the heading of the document we're examinining
1921 (in other words, which of these links represent structural
1922 information about that document). It also looks at the
1923 items found at the end of these links to see if they are
1924 sections or notes (``noteness'' is determined by them
1925 having content). The links are then sorted by their
1926 middles (which show the order in which these components
1927 have in the section we're examining). After this ordering
1928 information has been used for sorting, it is deleted, and
1929 we're left with just a list of names in the appropriate
1930 order, together with an indication of their noteness.
1931 \end{notate}
1933 \begin{elisp}
1934 (defun get-section-contents (name heading)
1935 (let (contents)
1936 (dolist (triple (triples-given-beginning
1937 `(1 ,(resolve-ambiguity
1938 (get-places name)))))
1939 (when (triple-exact-match
1940 `(2 ,(car triple)) "in" heading)
1941 (let* ((number (print-middle triple))
1942 (site (isolate-end triple))
1943 (noteness
1944 (when (triples-given-beginning-and-middle
1945 site "has content")
1946 t)))
1947 (setq contents
1948 (cons (list number
1949 (print-system-object
1950 (place-contents site))
1951 noteness)
1952 contents)))))
1953 (mapcar 'cdr
1954 (sort contents
1955 (lambda (component1 component2)
1956 (< (parse-integer (car component1))
1957 (parse-integer (car component2))))))))
1958 \end{elisp}
1960 \begin{notate}{On `format-section-contents'} \label{format-section-contents}
1961 A formatter for document contents, used by
1962 `display-document' (Note \ref{display-document}) as input
1963 for `pick-a-name' (Note \ref{pick-a-name}).
1965 Instead of just printing the items one by one,
1966 like the default formatter in `pick-a-name' does,
1967 this version adds appropriate text properties, which
1968 we determine based the second component of
1969 of `items' to format.
1970 \end{notate}
1972 \begin{elisp}
1973 (defun format-section-contents (items heading)
1974 ;; just replicating the default and building on that.
1975 (mapc (lambda (item)
1976 (insert (car item))
1977 (let* ((beg (line-beginning-position))
1978 (end (1+ beg)))
1979 (unless (second item)
1980 (put-text-property beg end
1981 'arxana-display-type
1982 'section))
1983 (put-text-property beg end
1984 'arxana-relevant-heading
1985 heading))
1986 (insert "\n"))
1987 items))
1988 \end{elisp}
1990 \begin{notate}{On `display-section'} \label{display-section}
1991 When browsing a document, if you select a section, you
1992 should display a list of that section's constituent
1993 elements, be they notes or subsections. The question
1994 comes up: when you go to display something, how do you
1995 know whether you're looking at the name of a section, or
1996 the name of an article?
1998 When you get the section's contents out of the database
1999 (Note \ref{get-section-contents})
2000 \end{notate}
2002 \begin{elisp}
2003 (defun display-section (name heading)
2004 (interactive (list (read-string
2005 (concat
2006 "name (default "
2007 (buffer-name) "): ")
2008 nil nil (buffer-name))))
2009 ;; should this pop to the Articles window?
2010 (pick-a-name `(lambda ()
2011 (get-section-contents
2012 ,name ,heading))
2013 `(lambda (items)
2014 (format-section-contents
2015 items ,heading))))
2017 (add-to-list 'display-style
2018 '(section . (display-section
2019 nil)))
2020 \end{elisp}
2022 \begin{notate}{On `display-document'} \label{display-document}
2023 When browsing a document, you should first display its
2024 top-level table of contents. (Most typically, a list of
2025 all of that document's major sections.) In order to do
2026 this, we must find the triples that are begin at the node
2027 representing this document \emph{and} that are in the
2028 heading of this document. This boils down to treating the
2029 document's root as if it was a section and using the
2030 function `display-section' (Note \ref{display-section}).
2031 \end{notate}
2033 \begin{elisp}
2034 (defun display-document (name)
2035 (interactive (list (read-string
2036 (concat
2037 "name (default "
2038 (buffer-name) "): ")
2039 nil nil (buffer-name))))
2040 (display-section name name))
2041 \end{elisp}
2043 \begin{notate}{Work with `heading' argument}
2044 We should make sure that if we know the heading we're
2045 working with (e.g. the name of the document we're
2046 browsing) that this information gets communicated in the
2047 background of the user interaction with the article
2048 selector.
2049 \end{notate}
2051 \begin{notate}{Selecting from a hierarchical display} \label{hierarchical-display}
2052 A fancier ``article selector'' would be able to display
2053 several sections with nice indenting to show their
2054 hierarchical order.
2055 \end{notate}
2057 \begin{notate}{Browser history tricks} \label{history-tricks}
2058 I want to put together (or put back together) something
2059 similar to the multihistoried browser that I had going in
2060 the previous version of Arxana and my Emacs/Lynx-based web
2061 browser, Nero\footnote{{\tt http://metameso.org/~joe/nero.el}}.
2062 The basic features are:
2063 (1) forward, back, and up inside the structure of a given
2064 document; (2) switch between tabs. More advanced features
2065 might include: (3) forward and back globally across all
2066 tabs; (4) explicit understanding of paths that loop.
2068 These sorts of features are independent of the exact
2069 details of what's printed to the screen each time
2070 something is displayed. So, for instance, you could flip
2071 between section manifests a la Note \ref{display-section},
2072 or between hierarchical displays a la Note
2073 \ref{hierarchical-display}, or some combination; the key
2074 thing is just to keep track in some sensible way of
2075 whatever's been displayed!
2076 \end{notate}
2078 \begin{notate}{Local storage for browsing purposes} \label{local-storage}
2079 Right now, in order to browse the contents of the
2080 database, you need to query the database every time. It
2081 might be handy to offer the option to cache names of
2082 things locally, and only sync with the database from time
2083 to time. Indeed, the same principle could apply in
2084 various places; however, it may also be somewhat
2085 complicated to set up. Using two systems for storage, one
2086 local and one permanent, is certainly more heavy-duty than
2087 just using one permanent storage system and the local
2088 temporary display. However, one thing in favor of local
2089 storage systems is that that's what I used in the the
2090 previous prototype of Arxana -- so some code already
2091 exists for local storage! (Caching the list of
2092 \emph{names} we just made a selection from would be one
2093 simple expedient, see Note \ref{pick-a-name}.)
2094 \end{notate}
2096 \begin{notate}{Hang onto absolute references}
2097 Since `get-article' (Note \ref{get-article}) translates
2098 strings into their ``place pseudonyms'', we may want to
2099 hang onto those pseudonyms, because they are, in fact, the
2100 absolute references to the objects we end up working with.
2101 In particular, they should probably go into the
2102 text-property background of the article selector, so it
2103 will know right away what to select!
2104 \end{notate}
2106 \subsection{Exporting \LaTeX\ documents$^*$}
2108 \begin{notate}{Roundtripping}
2109 The easiest test is: can we import a document into the
2110 system and then export it again, and find it unchanged?
2111 \end{notate}
2113 \begin{notate}{Data format}
2114 We should be able to \emph{stably} import and export a
2115 document, as well as export any modifications to the
2116 document that were generated within Arxana. This means
2117 that the exporting functions will have to read the data
2118 format that the importing functions use, \emph{and} that
2119 any functions that edit document contents (or structure)
2120 will also have to use the same format. Furthermore,
2121 \emph{browsing} functions will have to be somewhat aware
2122 of this format. So, this is a good time to ask -- did we
2123 use a good format?
2124 \end{notate}
2126 \subsection{Editing database contents$^*$} \label{editing}
2128 \begin{notate}{Roundtripping, with changes}
2129 Here, we should import a document into the system and then
2130 make some simple changes, and after exporting, check with
2131 diff to make sure the changes are correct.
2132 \end{notate}
2134 \begin{notate}{Re-importing}
2135 One nice feature would be a function to ``re-import'' a
2136 document that has changed outside of the system, and make
2137 changes in the system's version whereever changes appeared
2138 in the source version.
2139 \end{notate}
2141 \begin{notate}{Editing document structure}
2142 The way we have things set up currently, it is one thing
2143 to make a change to a document's textual components, and
2144 another to change its structure. Both types of changes
2145 must, of course, be supported.
2146 \end{notate}
2148 \section{Applications} \label{applications}
2150 \subsection{Managing tasks} \label{managing-tasks}
2152 \begin{notate}{What are tasks?}
2153 Each task tends to have a \emph{name}, a
2154 \emph{description}, a collection of \emph{prerequisite
2155 tasks}, a description of other \emph{material
2156 dependencies}, a \emph{status}, some \emph{justification
2157 of that status}, a \emph{creation date}, and an
2158 \emph{estimated time of completion}. There might actually
2159 be several ``estimated times of completion'', since the
2160 estimate would tend to improve over time. To really
2161 understand a task, one should keep track of revisions like
2162 this.
2163 \end{notate}
2165 \begin{notate}{On `store-task-data'} \label{store-task-data}
2166 Here, we're just filling in a frame. Since ``filling in a
2167 frame'' seems like the sort of operation that might happen
2168 over and over again in different contexts, to save space,
2169 it would probably be nice to have a macro (or similar)
2170 that would do a more general version of what this function
2171 does.
2172 \end{notate}
2174 \begin{elisp}
2175 (defun store-task-data
2176 (name description prereqs materials status
2177 justification submitted eta)
2178 (put-nema name "is a" "task")
2179 (put-nema name "description" description)
2180 (put-nema name "prereqs" prereqs)
2181 (put-nema name "materials" materials)
2182 (put-nema name "status" status)
2183 (put-nema name "status justification" justification)
2184 (put-nema name "date submitted" submitted)
2185 (put-nema name "estimated time of completion" eta))
2186 \end{elisp}
2188 \begin{notate}{On `generate-task-data'} \label{generate-task-data}
2189 This is a simple function to create a new task matching
2190 the description above.
2191 \end{notate}
2193 \begin{elisp}
2194 (defun generate-task-data ()
2195 (interactive)
2196 (let ((name (read-string "Name: "))
2197 (description (read-string "Description: "))
2198 (prereqs (read-string
2199 "Task(s) this task depends on: "))
2200 (materials (read-string "Material dependencies: "))
2201 (status (completing-read
2202 "Status (tabled, in progress, completed):
2203 " '("tabled" "in progress" "completed")))
2204 (justification (read-string "Why this status? "))
2205 (submitted
2206 (read-string
2207 (concat "Date submitted (default "
2208 (substring (current-time-string) 0 10)
2209 "): ")
2210 nil nil (substring (current-time-string) 0 10)))
2211 (eta
2212 (read-string "Estimated date of completion:")))
2213 (store-task-data name description prereqs materials
2214 status
2215 justification submitted eta)))
2216 \end{elisp}
2218 \begin{notate}{Possible enhancements to `generate-task-data'}
2219 In order to make this function very nice, it would be good
2220 to allow ``completing read'' over known tasks when filling
2221 in the prerequisites. Indeed, it might be especially nice
2222 to offer a type of completing read that is similar in some
2223 sense to the tab-completion you get when completing a file
2224 name, i.e., quickly completing certain sub-strings of the
2225 final string (in this case, these substrings would
2226 correspond to task areas we are progressively zooming down
2227 into).
2229 As for the task description, rather than forcing the user
2230 to type the description into the minibuffer, it might be
2231 nice to pop up a separate buffer instead (a la the
2232 Emacs/w3m textarea). If we had a list of all the known
2233 tasks, we could offer completing-read over the names of
2234 existing tasks to generate the list of `prereqs'. It
2235 might be nice to systematize date data, so we could more
2236 easily e.g. sort and display task info ``by date''.
2237 (Perhaps we should be working with predefined database
2238 types for dates and so on.)
2240 Also, before storing the task, it might be nice to offer
2241 the user the chance to review the data they entered.
2242 \end{notate}
2244 \begin{notate}{On `get-filler'} \label{get-filler}
2245 Just a wrapper for `triples-given-beginning-and-middle'.
2246 (Maybe we should add `heading' as an optional argument here.)
2247 \end{notate}
2249 \begin{elisp}
2250 (defun get-filler (frame slot)
2251 (third (first
2252 (print-triples
2253 (triples-given-beginning-and-middle frame
2254 slot)))))
2255 \end{elisp}
2257 \begin{notate}{On `get-task'} \label{get-task}
2258 Uses `get-filler' (Note \ref{get-filler}) to assemble the
2259 elements of a task's frame.
2260 \end{notate}
2262 \begin{elisp}
2263 (defun get-task (name)
2264 (when (triple-exact-match name "is a" "task")
2265 (list (get-filler name "description")
2266 (get-filler name "prereqs")
2267 (get-filler name "materials")
2268 (get-filler name "status")
2269 (get-filler name "status justification")
2270 (get-filler name "date submitted")
2271 (get-filler name
2272 "estimated time of completion"))))
2273 \end{elisp}
2275 \begin{notate}{On `review-task'} \label{review-task}
2276 This is a function to review a task by name.
2277 \end{notate}
2279 \begin{elisp}
2280 (defun review-task (name)
2281 (interactive "MName: ")
2282 (let ((task-data (get-task name)))
2283 (if task-data
2284 (display-task task-data)
2285 (message "No data."))))
2287 (defun display-task (data)
2288 (save-excursion
2289 (pop-to-buffer (get-buffer-create
2290 "*Arxana Display*"))
2291 (delete-region (point-min) (point-max))
2292 (insert "NAME: " name "\n\n")
2293 (insert "DESCRIPTION: " (first data) "\n\n")
2294 (insert "TASKS THIS TASK DEPENDS ON: "
2295 (second data) "\n\n")
2296 (insert "MATERIAL DEPENDENCIES: "
2297 (third data) "\n\n")
2298 (insert "STATUS: " (fourth data) "\n\n")
2299 (insert "WHY THIS STATUS?: " (fifth data) "\n\n")
2300 (insert "DATE SUBMITTED:" (sixth data) "\n\n")
2301 (insert "ESTIMATED TIME OF COMPLETION: "
2302 (seventh data) "\n\n")
2303 (goto-char (point-min))
2304 (fill-individual-paragraphs (point-min) (point-max))))
2305 \end{elisp}
2307 \begin{notate}{Possible enhancements to `review-task'}
2308 Breaking this down into a function to select the task and
2309 another function to display the task would be nice. Maybe
2310 we should have a generic function for selecting any object
2311 ``by name'', and then special-purpose functions for
2312 displaying objects with different properties.
2314 Using text properties, we could set up a ``field-editing
2315 mode'' that would enable you to select a particular field
2316 and edit it independently of the others. Another more
2317 complex editing mode would \emph{know} which fields the
2318 user had edited, and would store all edits back to the
2319 database properly. See Section \ref{editing} for more on
2320 editing.
2321 \end{notate}
2323 \begin{notate}{Browsing tasks} \label{browsing-tasks}
2324 The function `pick-a-name' (Note \ref{pick-a-name}) takes
2325 two functions, one that finds the names to choose from,
2326 and the other that says how to present these names. We
2327 can therefore build `pick-a-task' on top of `pick-a-name'.
2328 \end{notate}
2330 \begin{elisp}
2331 (defun get-tasks ()
2332 (mapcar #'first
2333 (print-triples
2334 (triples-given-middle-and-end "is a" "task")
2335 t)))
2337 (defun pick-a-task ()
2338 (interactive)
2339 (pick-a-name
2340 'get-tasks
2341 (lambda (items)
2342 (mapc (lambda (item)
2343 (let ((pos (line-beginning-position)))
2344 (insert item)
2345 (put-text-property pos (1+ pos)
2346 'arxana-display-type
2347 'task)
2348 (insert "\n"))) items))))
2350 (add-to-list 'display-style
2351 '(task . (get-task display-task)))
2352 \end{elisp}
2354 \begin{notate}{Working with theories}
2355 Presumably, like other related functions, `get-tasks'
2356 should take a heading argument.
2357 \end{notate}
2359 \begin{notate}{Check display style}
2360 Check if this works, and make style consistent between
2361 this usage and earlier usage.
2362 \end{notate}
2364 \begin{notate}{Example tasks}
2365 It might be fun to add some tasks associated with
2366 improving Arxana, just to show that it can be done...
2367 maybe along with a small importer to show how importing
2368 something without a whole lot of structure can be easy.
2369 \end{notate}
2371 \begin{notate}{Org mode integration}
2372 The ``default'' task manager on Emacs is Org mode. It would be good
2373 to provide integration between Org mode and Arxana. This is one of
2374 the first things that Emacs people ask about when they hear about
2375 Arxana.
2376 \end{notate}
2378 \subsection{``Modelling mathematics the way it is really done''} \label{farm-demo}
2380 \begin{notate}{A demonstration}
2381 In a paper for the 5th ACM SIGPLAN International Workshop on
2382 Functional Art, Music, Modelling and Design (FARM 2017), we talk about
2383 how Arxana can be applied to model mathematical proofs. A rather
2384 advanced form of ``mathematical knowledge management'' is proposed,
2385 that integrates dialogue, heuristics, and that ultimately moves in the
2386 direction of an AI system. In this section, we should walk through
2387 this application.
2388 \end{notate}
2390 \section{Conclusion} \label{conclusion}
2392 \begin{notate}{Ending and beginning again}
2393 This is the end of this Arxana demo system. Contributions that
2394 support the development of the Arxana project are welcome.
2395 \end{notate}
2397 \appendix
2399 \section{Appendix: A simple literate programming system} \label{appendix-lit}
2401 \begin{notate}{The literate programming system used in this paper}
2402 This code defines functions that grab all the Lisp portions of this
2403 document, and evaluates the Emacs Lisp sections. It requires that the
2404 \LaTeX\ be written in a certain consistent way. The function assumes
2405 that this document is the current buffer.
2407 \begin{verbatim}
2408 (defvar lit-code-beginning-regexp
2409 "^\\\\begin{elisp}")
2411 (defvar lit-code-end-regexp
2412 "^\\\\end{elisp}")
2414 (defun lit-eval ()
2415 (interactive)
2416 (lit-process 'eval))
2418 (defun lit-process (&optional code)
2419 (interactive)
2420 (setq lit-count (1+ lit-count))
2421 (save-excursion
2422 (let ((to-buffer (concat "*Lit Code " (int-to-string
2423 lit-count)"*"))
2424 (from-buffer (buffer-name (current-buffer))))
2425 (set-buffer (get-buffer-create to-buffer))
2426 (erase-buffer)
2427 (set-buffer (get-buffer-create from-buffer))
2428 (goto-char (point-min))
2429 (while (re-search-forward
2430 lit-code-beginning-regexp nil t)
2431 (let* ((beg (match-end 0))
2432 (end (save-excursion
2433 (search-forward-regexp
2434 lit-code-end-regexp nil t)
2435 (match-beginning 0)))
2436 (match (buffer-substring beg end)))
2437 (save-excursion
2438 (set-buffer to-buffer)
2439 (insert match))))
2440 (case code
2441 ('eval
2442 (set-buffer to-buffer)
2443 (eval-buffer)
2444 (kill-buffer (current-buffer)))
2446 (switch-to-buffer to-buffer))))))
2447 \end{verbatim}
2448 \end{notate}
2450 \begin{notate}{A literate style}
2451 Ideally, each function will have its own Note to introduce
2452 it, and will not be called before it has been defined. I
2453 sometimes make an exception to this rule, for example,
2454 functions used to form recursions may appear with no
2455 further introduction, and may be called before they are
2456 defined.
2457 \end{notate}
2459 \end{document}