reorder bulk vs individual operations
[arxana.git] / latex / arxana-merge.tex
blob0f6e28aae0ecbc188e30d4a49ba12c96cd80bee0
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/latex/arxana-merge.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-plexus 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 the identifier that tells us 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 (1+ (cadr current-plexus)))
356 \end{elisp}
358 \begin{notate}{On `reset-plexus'}
359 Reset article counter and hash tables. Redefine ``ground'' and
360 ``article-type''.
361 \end{notate}
363 \begin{elisp}
364 (defun reset-plexus ()
365 "Reset the database to its initial configuration."
366 ; Reset nema counter and hash tables.
367 (setcar (cdr current-plexus) 1)
368 (dotimes (n 5)
369 (clrhash (nth (+ n 2) current-plexus)))
370 ;; Define ground and nema-type.
371 (puthash 0 '(0 0) (nth 2 current-plexus))
372 (puthash 1 '(0 0) (nth 2 current-plexus))
373 (puthash 0 '((0 . 0) (1 . 0)) (nth 3 current-plexus))
374 (puthash 0 '((0 . 0) (1 . 0)) (nth 4 current-plexus))
375 (puthash 0 '"ground" (nth 5 current-plexus))
376 (puthash '"ground" 0 (nth 6 current-plexus))
377 (puthash 1 '"nema-type" (nth 5 current-plexus))
378 (puthash '"nema-type" 1 (nth 6 current-plexus))
379 nil)
380 \end{elisp}
382 \subsection{Individual Operations}
384 \begin{notate}{On `add-nema'}
385 Add record to article table.
386 Add record to list of forward links of source.
387 Add record to list of backward links of sink.
388 Return the id of the new article.\todo{Should we add an alias `add-triple'
389 for this function, to make it more clear that our middle/frontend
390 is not implementation specific?}
391 \end{notate}
393 \begin{elisp}
394 (defun add-nema (src txt snk)
395 "Enter a new nema to the database."
396 (let ((uid (next-unique-id)))
397 ;; Add record to nema table.
398 (puthash uid
399 `(,src ,snk . ,txt)
400 (nth 2 current-plexus))
401 ;; Add record to list of forward links of source.
402 (puthash src
403 (cons `(,uid . ,snk)
404 (gethash src (nth 3 current-plexus) nil))
405 (nth 3 current-plexus))
406 ;; Add record to list of backward links of sink.
407 (puthash snk
408 (cons
409 `(,uid . ,src)
410 (gethash snk (nth 4 current-plexus) nil))
411 (nth 4 current-plexus))
412 ;; Return the id of the new nema.
413 uid))
414 \end{elisp}
416 \begin{notate}{Retrieving elements of a nema}
417 These functions exist to get the relevant components
418 of a nema, given its uid.
419 \end{notate}
421 \begin{elisp}
422 (defun get-content (uid)
423 "Return the content of the nema."
424 (cddr (gethash uid (nth 2 current-plexus))))
426 (defun get-source (uid)
427 "Return the source of the nema."
428 (car (gethash uid (nth 2 current-plexus))))
430 (defun get-sink (uid)
431 "Return the sink of the nema."
432 (cadr (gethash uid (nth 2 current-plexus))))
433 \end{elisp}
435 \begin{notate}{On `update-content'}
436 old source
437 old sink
438 new content
439 \end{notate}
441 \begin{elisp}
442 (defun update-content (uid txt)
443 "Replace the content of the nema."
444 (puthash uid
445 (let ((x (gethash uid (nth 2 current-plexus))))
446 `(,(car x) ; old source
447 ,(cadr x) . ; old sink
448 ,txt)) ; new content
449 (nth 2 current-plexus)))
450 \end{elisp}
452 \begin{notate}{On `update-source'}
453 Extract current source.
454 Extract current sink.
455 Extract current content.
456 Update the entry in the article table.
457 Remove the entry with the old source in the
458 forward link table. If that is the only entry
459 filed under old-src, remove it from table.
460 Add an entry with the new source in the
461 forward link table.
462 Update the entry in the backward link table.
463 \end{notate}
465 \begin{elisp}
466 (defun update-source (uid new-src)
467 "Replace the source of the nema."
468 (let* ((x (gethash uid (nth 2 current-plexus)))
469 (old-src (car x)) ; extract current source
470 (old-snk (cadr x)) ; extract current sink
471 (old-txt (cddr x))) ; extract current content
472 ;; Update the entry in the nema table.
473 (puthash uid
474 `(,new-src ,old-snk . ,old-txt)
475 (nth 2 current-plexus))
476 ;; Remove the entry with the old source in the
477 ;; forward link table. If that is the only entry
478 ;; filed under old-src, remove it from table.
479 (let ((y (delete `(,uid . ,old-snk)
480 (gethash old-src
481 (nth 3 current-plexus)
482 nil))))
483 (if y
484 (puthash old-src y (nth 3 current-plexus))
485 (remhash old-src (nth 3 current-plexus))))
486 ;; Add an entry with the new source in the
487 ;; forward link table.
488 (puthash new-src
489 (cons `(,uid . ,old-snk)
490 (gethash old-src (nth 3 current-plexus) nil))
491 (nth 3 current-plexus))
492 ;; Update the entry in the backward link table.
493 (puthash old-snk
494 (cons `(,uid . ,new-src)
495 (delete `(,uid . ,old-src)
496 (gethash old-src
497 (nth 4 current-plexus)
498 nil)))
499 (nth 4 current-plexus))))
500 \end{elisp}
502 \begin{notate}{On `update-sink'} \label{update-sink}
503 Extract current source.
504 Extract current sink.
505 Extract current content.
506 Update the entry in the article table.
507 Remove the entry with the old sink in the
508 backward link table. If that is the only entry
509 filed under old-src, remove it from table.
510 Add an entry with the new source in the
511 backward link table.
512 Update the entry in the forward link table.
513 \end{notate}
515 \begin{elisp}
516 (defun update-sink (uid new-snk)
517 "Change the sink of the nema."
518 (let* ((x (gethash uid (nth 2 current-plexus)))
519 (old-src (car x)) ; extract current source
520 (old-snk (cadr x)) ; extract current sink
521 (old-txt (cddr x))) ; extract current content
522 ; Update the entry in the nema table.
523 (puthash uid
524 `(,old-src ,new-snk . ,old-txt)
525 (nth 2 current-plexus))
526 ;; Remove the entry with the old sink in the
527 ;; backward link table. If that is the only entry
528 ;; filed under old-src, remove it from table.
529 (let ((y (delete `(,uid . ,old-src)
530 (gethash old-snk
531 (nth 4 current-plexus)
532 nil))))
533 (if y
534 (puthash old-snk y (nth 4 current-plexus))
535 (remhash old-snk (nth 4 current-plexus))))
536 ;; Add an entry with the new source in the
537 ;; backward link table.
538 (puthash new-snk
539 (cons `(,uid . ,old-src)
540 (gethash old-snk
541 (nth 4 current-plexus)
542 nil))
543 (nth 4 current-plexus))
544 ;; Update the entry in the forward link table.
545 (puthash old-src
546 (cons `(,uid . ,new-snk)
547 (delete `(,uid . ,old-snk)
548 (gethash old-src
549 (nth 3 current-plexus)
550 nil)))
551 (nth 3 current-plexus))))
552 \end{elisp}
554 \begin{notate}{On `remove-nema'}
555 Remove forward link created by article.
556 Remove backward link created by article.
557 Remove record from article table.
558 \end{notate}
560 \begin{elisp}
561 (defun remove-nema (uid)
562 "Remove this nema from the database."
563 (let ((old-src (car (gethash uid (nth 2 current-plexus))))
564 (old-snk (cadr (gethash uid (nth 2 current-plexus)))))
565 ;; Remove forward link created by nema.
566 (let ((new-fwd (delete `(,uid . ,old-snk)
567 (gethash old-src (nth 3 current-plexus)))))
568 (if new-fwd
569 (puthash old-src new-fwd (nth 3 current-plexus))
570 (remhash old-src (nth 3 current-plexus))))
571 ;; Remove backward link created by nema.
572 (let ((new-bkw (delete `(,uid . ,old-src)
573 (gethash old-snk (nth 4 current-plexus)))))
574 (if new-bkw
575 (puthash old-snk new-bkw (nth 4 current-plexus))
576 (remhash old-snk (nth 4 current-plexus))))
577 ;; Remove record from nema table.
578 (remhash uid (nth 2 current-plexus))))
579 \end{elisp}
581 \begin{notate}{Functions for gathering links}
582 Links are stored on triples alongside other
583 elements.
584 \end{notate}
586 \begin{elisp}
587 (defun get-forward-links (uid)
588 "Return all links having given object as source."
589 (mapcar 'car (gethash uid (nth 3 current-plexus))))
591 (defun get-backward-links (uid)
592 "Return all links having given object as sink."
593 (mapcar 'car (gethash uid (nth 4 current-plexus))))
594 \end{elisp}
596 \begin{notate}{On `label-nema'}
597 Nemas can be given a unique human-readable label in addition
598 to their numeric uid.
599 \end{notate}
601 \begin{elisp}
602 (defun label-nema (uid label)
603 "Assign the label to the given object."
604 (puthash uid label (nth 5 current-plexus))
605 (puthash label uid (nth 6 current-plexus)))
606 \end{elisp}
608 \begin{notate}{Label to uid and uid to label lookup}
609 These functions allow the exchange of uid and label.
610 \end{notate}
612 \begin{elisp}
613 (defun label2uid (label)
614 "Return the unique identifier corresponding to a label."
615 (gethash label (nth 6 current-plexus) nil))
617 (defun uid2label (uid)
618 "Return the label associated to a unique identifier."
619 (gethash uid (nth 5 current-plexus) nil))
620 \end{elisp}
622 \subsection{Bulk Operations}
624 \begin{notate}{On `download-en-masse'}
625 Unpack triplets, obtain labels if they exist.
626 Write data in the network to a list, and return.
627 \end{notate}
629 \begin{elisp}
630 (defun download-en-masse ()
631 "Produce a representation of the database as quintuples."
632 (let ((plex nil))
633 (maphash (lambda (uid tplt)
634 ; Unpack triplet.
635 (let ((src (car tplt))
636 (snk (nth 1 tplt))
637 (txt (nthcdr 2 tplt)))
638 ; Obtain next label if exists.
639 (setq lbl (gethash uid
640 (nth 5 current-plexus)
641 nil))
642 ; Write data to list.
643 (setq plex (cons `(,uid ,lbl ,src ,snk . ,txt)
644 plex))))
645 (nth 2 current-plexus))
646 ; Return list of data.
647 (reverse plex)))
648 \end{elisp}
650 \begin{notate}{On `upload-en-masse'}
651 Unpack quintuplets. Plug into tables.
652 Bump up article counter as needed.
653 \end{notate}
655 \begin{elisp}
657 (defun upload-en-masse (plex)
658 "Load a representation of a database as quintuples into memory."
659 (dolist (qplt plex t)
660 ; unpack quintuplet
661 (let ((uid (car qplt))
662 (lbl (nth 1 qplt))
663 (src (nth 2 qplt))
664 (snk (nth 3 qplt))
665 (txt (nthcdr 4 qplt)))
666 ; plug into tables
667 (puthash uid
668 `(,src ,snk . ,txt)
669 (nth 2 current-plexus))
670 (puthash src
671 (cons `(,uid . ,snk)
672 (gethash src (nth 3 current-plexus) nil))
673 (nth 3 current-plexus))
674 (puthash snk
675 (cons
676 `(,uid . ,src)
677 (gethash snk (nth 4 current-plexus) nil))
678 (nth 4 current-plexus))
679 (when lbl
680 (progn
681 (puthash uid lbl (nth 5 current-plexus))
682 (puthash lbl uid (nth 6 current-plexus))))
683 ; Bump up nema counter if needed.
684 (when (> uid (cadr current-plexus))
685 (setcar (cdr current-plexus) uid)))))
686 \end{elisp}
688 \begin{notate}{On `add-en-masse'}
689 Given several articles, add all of them at once.
690 \end{notate}
692 \begin{elisp}
693 (defun add-en-masse (plex)
694 "Add multiple nemata given as list of quartuplets."
695 (mapcar (lambda (qplt)
696 (let ((uid (next-unique-id)))
697 (add-nema (nth 1 plex)
698 (nthcar 2 plex)
699 (nth 2 plex))
700 (label-nema uid (car qplt))))
701 plex))
702 \end{elisp}
704 \subsection{Query}
706 \begin{notate}{Overview of search and query functionality}
707 We first describe several elementary functions for
708 accessing elements of the network. We then describe a
709 robust search pipeline and show how it is implemented.
710 \end{notate}
712 \begin{notate}{Various lookup functions}
713 These functions allow testing and lookup of various elements
714 of a net.
715 \end{notate}
717 \begin{elisp}
718 (defun uid-p (uid)
719 "Is this a valid uid?"
720 (let ((z '(())))
721 (not (eq z (gethash uid (nth 2 current-plexus) z)))))
723 (defun uid-list ()
724 "List of all valid uid's."
725 (maphash (lambda (key val) key)
726 (nth 2 current-plexus)))
728 (defun ground-p (uid)
729 "Is this nema the ground?"
730 (= uid 0))
732 (defun source-p (x y)
733 "Is the former nema the sink of the latter?"
734 (equal x (get-source y)))
736 (defun sink-p (x y)
737 "Is the former nema the sink of the latter?"
738 (equal x (get-sink y)))
740 (defun links-from (x y)
741 "Return all links from nema x to nema y."
742 (filter '(lambda (z) (source-p x z))
743 (get-backward-links y)))
745 (defun links-p (x y)
746 "Does nema x link to nema y?"
747 (when (member x (mapcar
748 'get-source
749 (get-backward-links y)))
752 (defun triple-p (x y z)
753 "Do the three items form a triplet?"
754 (and (source-p y x)
755 (sink-p y z)))
757 (defun plexus-p (x)
758 "Is this object a plexus?"
759 (let ((ans t))
760 (setq ans (and ans
761 (equal (car x) "*plexus*")))
762 (setq ans (and ans
763 (integrp (cadr x))))
764 (dotimes (n 5)
765 (setq ans (and ans (hash-table-p
766 (nth (+ n 2) x)))))
767 ans))
768 \end{elisp}
770 \subsection{Iteration}
772 \begin{notate}{Iterating over a plexus}
773 These functions allow users to run loops over a plexus without
774 having to delve into its internal structure.
775 \end{notate}
777 \begin{elisp}
778 (defmacro do-plexus (var res body)
779 `((maphash (lambda (,var val) ,body)
780 (nth 2 current-plexus))
781 ,res))
783 (defun map-plexus (func)
784 (let ((ans nil))
785 (maphash
786 (lambda (key val)
787 (push (apply func key) ans))
788 (nth 2 current-plexus))
789 ans))
791 (defun filter-plexus (pred)
792 (let ((ans nil))
793 (maphash
794 (lambda (key val)
795 (when (apply pred key)
796 (push key ans)))
797 (nth 2 current-plexus))
798 ans))
799 \end{elisp}
801 \begin{notate}{Additional elementary functions for node access}
802 These functions give access to the various parts of a node.\todo{Note:
803 `article-list' hasn't been defined at this point, what is it? Is it
804 something we can straightforwardly derive from `current-plexus'}
805 \end{notate}
807 \begin{elisp}
808 (defun get-src (n)
809 (car (nth 0 (cdr (assoc n (cdr article-list))))))
811 (defun get-flk (n)
812 (cdr (nth 0 (cdr (assoc n (cdr article-list))))))
814 (defun get-txt (n)
815 (nth 1 (cdr (assoc n (cdr article-list)))))
817 (defun get-snk (n)
818 (car (nth 2 (cdr (assoc n (cdr article-list))))))
820 (defun get-blk (n)
821 (cdr (nth 2 (cdr (assoc n (cdr article-list))))))
823 (defun get-ids nil
824 (mapcar (quote car) (cdr article-list)))
826 (defun get-gnd nil 0)
827 \end{elisp}
829 \begin{notate}{On `search-cond'} \label{search-cond}
830 Surround the search within dolist loops on free variables.
831 Wrap no further when finished.\todo{Upgrade this to concatenate the results together.
832 Also maybe allow options to add headers or to
833 only loop over unique tuplets.}\todo{Explain; how does this differ from
834 the function defined at Note \ref{search}?}
835 \end{notate}
837 \begin{elisp}
838 (defmacro search-cond (vars prop)
839 "Find all n-tuplets satisfying a condition"
840 (let ((foo '(lambda (vars cmnd)
841 (if vars
842 Wrap in a loop.
843 `(dolist (,(car vars) uids)
844 ,(funcall foo (cdr vars) cmnd))
845 cmnd))))
846 (funcall foo vars prop)))
847 \end{elisp}
849 \begin{notate}{Overview of the search pipeline}
850 We will implement the search as a pipeline which gradually
851 transforms the query into a series of expressions which produce
852 the sought-after result, then evaluate those expressions.
854 A search query designates predicates apply to the nodes
855 and the network relationships that apply to them. The network relationships
856 function as wildcards.
858 The basic model of the data is triplets that point to other triplets.
859 The following query asks for a \emph{funny} link from a
860 \emph{big blue object} to a \emph{small green link} pointing outwards
861 from the big blue object.
862 \begin{idea}
863 (((a blue big) (b funny) (c green small)
864 ((b src a) (b snk c) (c src a))
865 \end{idea}
866 The first step of processing is to put the quaerenda in some
867 order so that each item links up with at least one previous item:
868 \begin{idea}
869 (scheduler
870 '((b funny)
871 (c green small))
872 '((b src a)
873 (b snk c)
874 (c src a))
875 '((a blue big)))
877 ((c (green small) ((b snk c) (c src a)))
878 (b (funny) ((b src a)))
879 (a blue big))
880 \end{idea}
881 Note that the order is reversed due to technicalities of
882 implementing `scheduler' --- that is to say, a is first and does
883 not link to any other variable, b is next and links to only a,
884 whilst c is last and links to both a and b.
885 At the same time, we have also rearranged things so that the
886 links to previous items to which a given object are listed
887 alongside that object. The next step is to replace the links with the commands which
888 generate a list of such objects:
889 \begin{idea}
890 ((c (green small) ((b snk c) (c src a)))
891 (b (funny) ((b src a)))
892 (a blue big))
894 ((c (green small)
895 (intersection (list (get-snk b)) (get-forward-links a)))
896 (b (funny)
897 (intersection (get-backward-links a)))
898 (a blue big))
899 \end{idea}
900 This is done using the function `tplts2cmd', e.g.
901 \begin{idea}
902 (tplts2cmd 'c '((b snk c) (c src a)))
904 (intersection (list (get-snk b)) (get-forward-links a))
905 \end{idea}
906 Subsequently, we filter over the predicates:
907 \begin{idea}
908 ((c (filter '(lambda (c) (and (green c) (small c)))
909 (intersection (list (get-snk b))
910 (get-forward-links))))
911 (b (filter '(lambda (b) (and (funny b)))
912 (intersection (get-backward-links a)))))
913 \end{idea}
914 This is done with the command `add-filt':
915 \begin{idea}
916 (add-filt 'c
917 '(green small)
918 '((b snk c) (c src a)))
920 (c (filter (quote (lambda (c) (and (green c) (small c))))
921 (intersection (list (get-snk b))
922 (get-forward-links a))))
923 \end{idea}
924 This routine calls up the previously described routine `tplts2cmd'
925 to take care of the third argument. The last entry, {\tt (a blue big)}
926 gets processed a little differently because we don't as yet have
927 anything to filter over; instead, we generate the initial list by
928 looping over the current network:
929 \begin{idea}
930 (a (let ((ans nil))
931 (donet 'node
932 (when (and (blue (get-content node))
933 (big (get-content node)))
934 (setq ans (cons node ans))))
935 ans))
936 \end{idea}
937 This is done by invoking `first2cmd':
938 \begin{idea}
939 (first2cmd '(blue big))
941 (let ((ans nil))
942 (donet (quote node)
943 (when (and (blue (get-content node))
944 (big (get-content node)))
945 (setq ans (cons node ans))))
946 ans)
947 \end{idea}
948 And putting this all together:
949 \begin{idea}
950 (query2cmd
951 '((c (green small) ((b snk c) (c src a)))
952 (b (funny) ((b src a)))
953 (a blue big)))
955 ((c (filter (quote (lambda (c) (and (green c) (small c))))
956 (intersection (list (get-snk b))
957 (get-forward-links a))))
958 (b (filter (quote (lambda (b) (and (funny b))))
959 (intersection (get-forward-links a))))
960 (a (let ((ans nil))
961 (donet (quote node)
962 (when (and (blue (get-content node))
963 (big (get-content node)))
964 (setq ans (cons node ans))))
965 ans)))
966 \end{idea}
967 To carry out these instructions in the correct order and generate
968 a set of variable assignments, we employ the `matcher' function.
969 Combining this last layer, we have the complete pipeline:
970 \begin{idea}
971 (matcher nil
972 (query2cmd
973 (scheduler
974 '((b funny)
975 (c green small))
976 '((b src a)
977 (b snk c)
978 (c src a))
979 '((a blue big)))))
980 \end{idea}
981 This combination of operations is combined into the `search'
982 function, which can be called as follows:
983 \begin{idea}
984 (search
985 '(((a blue big)
986 (b funny)
987 (c green small))
988 ((b src a)
989 (b snk c)
990 (c src a))))
991 \end{idea}
993 Having described what the functions are supposed to do and how
994 they work together, we now proceed to implement them.
995 \end{notate}
997 \begin{notate}{On `scheduler'}
998 The scheduler function takes a list search query and rearranges it
999 into an order suitable for computing the answer to that query.
1000 Specifically, a search query is a pair of lists --- the first list
1001 consists of lists whose heads are names of variables and whose
1002 tails are predicates which the values of the variables should
1003 satisfy and the second list consists of triples indicating the
1004 relations between the values of the variables.
1005 Its arguments are:
1006 \begin{itemize}
1007 \item new-nodes, a list of items of the form \verb|(node &rest property)|;
1008 \item \verb|links|, a list of triplets;
1009 \item \verb|sched| is a list whose items consist of triplets of the
1010 form\newline \verb|(node (&rest property) (&rest link))|.
1011 \end{itemize}
1013 A recursive function to find linked nodes.
1014 If done, return answer.
1015 New nodes yet to be examined.
1016 Element of remaining-nodes currently under consideration.
1017 List of links between candidate and old-nodes.
1018 List of nodes already scheduled.
1019 Loop through new nodes until find one linked to an old node.
1020 Look at the next possible node.
1021 Find the old nodes linking to the candidate node and record the answer in ``ties''.
1022 Pick out the triplets whose first element is the node under consideration and whose third element is already on the list or vice-versa.
1023 Recursively add the rest of the nodes.
1024 \end{notate}
1026 \begin{elisp}
1027 (defun scheduler (new-nodes links sched)
1028 (if (null new-nodes)
1029 sched
1030 (let ((remaining-nodes new-nodes)
1031 (candidate nil)
1032 (ties nil)
1033 (old-nodes (mapcar 'car sched)))
1034 (while (null ties)
1035 (setq candidate (car remaining-nodes))
1036 (setq remaining-nodes (cdr remaining-nodes))
1037 (setq ties
1038 (filter '(lambda (x)
1040 (and (eq (first x) (car candidate))
1041 (member (third x) old-nodes))
1042 (and (member (first x) old-nodes)
1043 (eq (third x) (car candidate)))))
1044 links)))
1045 (scheduler (remove candidate new-nodes)
1046 links
1047 (cons (list (car candidate)
1048 (cdr candidate)
1049 ties)
1050 sched)))))
1051 \end{elisp}
1053 \begin{notate}{On `tplts2cmd'}
1054 \ldots\todo{Explain.}
1055 \end{notate}
1057 \begin{elisp}
1058 (defun tplts2cmd (var tplts)
1059 (cons 'intersection
1060 (mapcar
1061 #'(lambda (tplt)
1062 (cond ((and (eq (third tplt) var)
1063 (eq (second tplt) 'src))
1064 `(get-flk ,(first tplt)))
1065 ((and (eq (third tplt) var)
1066 (eq (second tplt) 'snk))
1067 `(get-blk ,(first tplt)))
1068 ((and (eq (first tplt) var)
1069 (eq (second tplt) 'src))
1070 `(list (get-src ,(third tplt))))
1071 ((and (eq (first tplt) var)
1072 (eq (second tplt) 'snk))
1073 `(list (get-snk ,(third tplt))))
1074 (t nil)))
1075 tplts)))
1076 \end{elisp}
1078 \begin{notate}{On `add-filt'}
1079 \ldots\todo{Explain.}
1080 \end{notate}
1082 \begin{elisp}
1083 (defun add-filt (var preds tplts)
1084 `(,var
1085 (filter
1086 #'(lambda (,var)
1087 ,(cons 'and
1088 (mapcar
1089 #'(lambda (pred)
1090 (list pred
1091 (list 'get-txt var)))
1092 preds)))
1093 ,(tplts2cmd var tplts))))
1094 \end{elisp}
1096 \begin{notate}{On `first2cmd'}
1097 \ldots\todo{Explain.}
1098 \end{notate}
1100 \begin{elisp}
1101 (defun first2cmd (preds)
1102 `(let ((ans nil))
1103 (dolist (node (get-ids) ans)
1104 (when
1105 ,(cons 'and
1106 (mapcar
1107 #'(lambda (pred)
1108 (cons pred '((get-txt node))))
1109 preds))
1110 (setq ans (cons node ans))))))
1111 \end{elisp}
1113 \begin{notate}{On `query2cmd'}
1114 \ldots\todo{Explain.}
1115 \end{notate}
1117 \begin{elisp}
1118 (defun query2cmd (query)
1119 (let ((backwards (reverse query)))
1120 (reverse
1121 (cons
1122 (list (caar backwards)
1123 (first2cmd (cdar backwards)))
1124 (mapcar
1125 #'(lambda (x)
1126 (add-filt (first x) (second x) (third x)))
1127 (cdr backwards))))))
1128 \end{elisp}
1130 \begin{notate}{On `matcher'}
1131 \ldots\todo{Explain.}
1132 \end{notate}
1134 \begin{elisp}
1135 (defun matcher (assgmt reqmts)
1136 (if (null reqmts) (list assgmt)
1137 (apply 'append
1138 (mapcar
1139 #'(lambda (x)
1140 (matcher (cons (list (caar reqmts) x)
1141 assgmt)
1142 (cdr reqmts)))
1143 (apply 'intersection
1144 (eval `(let ,assgmt
1145 (mapcar 'eval
1146 (cdar reqmts)))))))))
1147 \end{elisp}
1149 \begin{notate}{How matcher works}
1150 Here are some examples unrelated to what comes up in searching
1151 triplets which illustrate how matcher works:
1152 \end{notate}
1154 \begin{idea}
1155 (matcher '((x 1)) '((y (list 1 3))
1156 (z (list (+ x y) (- y x)))))
1158 (((z 2) (y 1) (x 1))
1159 ((z 0) (y 1) (x 1))
1160 ((z 4) (y 3) (x 1))
1161 ((z 2) (y 3) (x 1)))
1163 (matcher nil '((x (list 1))
1164 (y (list 1 3))
1165 (z (list (+ x y) (- y x)))))
1167 (((z 2) (y 1) (x 1))
1168 ((z 0) (y 1) (x 1))
1169 ((z 4) (y 3) (x 1))
1170 ((z 2) (y 3) (x 1)))
1171 \end{idea}
1173 \begin{notate}{On `search'} \label{search}
1174 \ldots\todo{Explain; how does this differ from
1175 the macro defined at Note \ref{search-cond}?}
1176 \end{notate}
1178 \begin{elisp}
1179 (defun search (query)
1180 (matcher nil
1181 (reverse
1182 (query2cmd
1183 (scheduler
1184 (cdar query)
1185 (cadr query)
1186 (list (caar query)))))))
1187 \end{elisp}
1189 \begin{notate}{Search convenience functions}
1190 Several convenience functions for search can be
1191 defined.\todo{The code here is just a sketch, but hopefully something similar will actually work!}
1192 \end{notate}
1194 \begin{elisp}
1195 (defun triples-given-beginning (node)
1196 "Get triples outbound from the given NODE."
1197 (filter-plexus `(((a ,node))
1198 ((c src a)
1199 (c snk b)))))
1201 (defun triples-given-end (node)
1202 "Get triples inbound into NODE."
1203 (filter-plexus `(((b ,node))
1204 ((c src a)
1205 (c snk b)))))
1207 (defun triples-given-middle (edge)
1208 "Get the triples that run along EDGE."
1209 (filter-plexus `(((c ,edge))
1210 ((c src a)
1211 (c snk b)))))
1213 (defun triples-given-middle-and-end (edge node)
1214 "Get the triples that run along EDGE into NODE."
1215 (filter-plexus `(((c ,edge) (b ,node))
1216 ((c src a)
1217 (c snk b)))))
1219 (defun triples-given-beginning-and-middle (node edge)
1220 "Get the triples that run from NODE along EDGE."
1221 (filter-plexus `(((a ,node) (c ,edge))
1222 ((c src a)
1223 (c snk b)))))
1225 (defun triples-given-beginning-and-end (node1 node2)
1226 "Get the triples that run from NODE1 to NODE2. Optional
1227 argument VIEW causes the results to be selected
1228 into a view with that name."
1229 (filter-plexus `(((a ,node1) (b ,node2))
1230 ((c src a)
1231 (c snk b)))))
1233 (defun triple-exact-match (node1 edge node2)
1234 "Get the triples that run from NODE1 along EDGE to
1235 NODE2."
1236 (filter-plexus `(((a ,node1) (b ,node2) (c ,edge))
1237 ((c src a)
1238 (c snk b)))))
1239 \end{elisp}
1241 \subsection{Scholium programming}
1243 \begin{notate}{Scholium programming}
1244 The next several functions allow us to store and retrieve code
1245 from inside of the network.
1246 \end{notate}
1248 \begin{notate}{On `node-fun'}
1249 \ldots\todo{Explain.}
1250 Produce a list of commands to produce temporary bindings.
1251 Produce a list of commands to reset function values.
1252 \end{notate}
1254 \begin{elisp}
1255 (defun node-fun (node get-code get-links)
1256 (let ((code (funcall get-code node))
1257 (links (funcall get-links node)))
1258 (list
1259 'lambda
1260 (car code)
1261 (cons
1262 'prog1
1263 (cons
1264 (append
1265 '(progn)
1266 (mapcar #'(lambda (x)
1267 `(fset ',(car x)
1268 (node-fun ,(cdr x)
1269 ',get-code
1270 ',get-links)))
1271 links)
1272 (cdr code))
1273 (mapcar #'(lambda (x)
1274 (if (fboundp (car x))
1275 `(fset ',(car x)
1276 ',(symbol-function (car x)))
1277 `(fmakunbound ',(car x))))
1278 links))))))
1279 \end{elisp}
1281 \begin{notate}{On `tangle-module'}
1282 Recursively replace the chunks to recover executable code.\todo{Explain.}
1283 \end{notate}
1285 \begin{elisp}
1286 (defun tangle-module (node get-cont ins-links)
1287 (insert-chunk
1288 (funcall get-cont node)
1289 (mapcar #'(lambda (x)
1290 (cons (car x)
1291 (tangle-module (cdr x)
1292 get-cont
1293 ins-links)))
1294 (funcall ins-links node))))
1295 \end{elisp}
1297 \begin{notate}{On `insert-chunk'}
1298 Given a node and an association list of replacement texts, insert
1299 the chunks at the appropriate places.
1300 \end{notate}
1302 \begin{elisp}
1303 (defun insert-chunk (body chunks)
1304 (cond ((null body) nil)
1305 ((null chunks) body)
1306 ((equal (car body) '*insert*)
1307 (cdr (assoc (cadr body) chunks)))
1308 (t (cons (insert-chunk (car body) chunks)
1309 (insert-chunk (cdr body) chunks)))))
1310 \end{elisp}
1312 \begin{notate}{Functions for rewriting nemas}
1313 Several functions for rewriting nemas.\todo{How does this stuff relate to what's
1314 going on in the vicinity of Note \ref{update-sink}?}
1315 \end{notate}
1317 \begin{elisp}
1318 (defun set-src (n x)
1319 (if (equal n 0)
1321 (progn (let ((old-backlink
1322 (nth 1 (assoc (get-src n)
1323 (cdr article-list)))))
1324 (setcdr old-backlink
1325 (delete n (cdr old-backlink))))
1326 (let ((new-backlink
1327 `(nth 1 (assoc x (cdr article-list)))))
1328 (setcdr new-backlink (cons n (cdr new-backlink))))
1329 (setcar (nth 1 (assoc n (cdr article-list))) x))))
1331 (defun set-txt (n x)
1332 (setcar (cdr (cdr (assoc n (cdr article-list)))) x))
1334 (defun set-snk (n x)
1335 (if (equal n 0)
1337 (progn (let ((old-backlink
1338 (nth 3 (assoc (get-snk n)
1339 (cdr article-list)))))
1340 (setcdr old-backlink
1341 (delete n (cdr old-backlink))))
1342 (let ((new-backlink
1343 (nth 3 (assoc x (cdr article-list)))))
1344 (setcdr new-backlink (cons n (cdr new-backlink))))
1345 (setcar (nth 3 (assoc n (cdr article-list))) x))))
1347 (defun ins-nod (src txt snk)
1348 (progn (setcdr article-list
1349 (cons (list (car article-list)
1350 (list src)
1352 (list snk))
1353 (cdr article-list)))
1354 (let ((backlink
1355 (nth 3 (assoc snk (cdr article-list)))))
1356 (setcdr backlink (cons (car article-list)
1357 (cdr backlink))))
1358 (let ((backlink
1359 (nth 1 (assoc src (cdr article-list)))))
1360 (setcdr backlink (cons (car article-list)
1361 (cdr backlink))))
1362 (- (setcar article-list (+ 1 (car article-list))) 1)))
1364 (defun del-nod (n)
1365 (if (or (equal n 0)
1366 (get-blk n)
1367 (get-flk n))
1369 (progn (let ((old-backlink
1370 (nth 3 (assoc (get-snk n)
1371 (cdr article-list)))))
1372 (setcdr old-backlink
1373 (delete n (cdr old-backlink))))
1374 (let ((old-backlink
1375 (nth 1 (assoc (get-src n)
1376 (cdr article-list)))))
1377 (setcdr old-backlink
1378 (delete n (cdr old-backlink))))
1379 (setcdr article-list
1380 (delete (assoc n (cdr article-list))
1381 (cdr article-list)))
1382 t)))
1383 \end{elisp}
1385 \subsection{Initialization}
1387 \begin{notate}{Initialize with a new network}
1388 For now, we just create one network to import things into. Additional
1389 networks can be added later (see Section \ref{applications}).
1390 \end{notate}
1392 \begin{elisp}
1393 (set-current-plexus (add-plexus))
1394 \end{elisp}
1396 \section{An example middle\"end} \label{middle-end}
1398 \begin{notate}{A middle\"end for managing collections of articles}
1399 This middle\"end is a set of functions that add triples into the
1400 backend. At this stage we basically ignore details of storage, and
1401 rely on the convenience functions defined above as the backend's API.
1402 In principle it would be possible to swap out the backend for another
1403 storage mechanism. We will give an example later on that uses more of
1404 the LISP-specific aspects of the backend implementation.\todo{Let's
1405 try to be a bit more concrete about this, especially in Section
1406 \ref{farm-demo}.} In this example, rather than talking about nemas
1407 and networks, we will talk about \emph{articles} and \emph{scholia}.
1408 These objects are things that user will want to access, create, and
1409 manipulate. However, we will deal with functions for user interaction
1410 (input, display, and editing) in Section \ref{frontend}, not here.
1411 Like the backend, the middle\"end could also be swapped out in
1412 applications where a different kind of data is modelled. And in fact,
1413 we come to some examples of other mid-level interfaces in Section
1414 \ref{applications}.
1415 \end{notate}
1417 \subsection{Database interaction} \label{interaction}
1419 \begin{notate}{The `article' function} \label{the-article-function}
1420 You can use this function to create an article with a
1421 given name and contents. You can optionally put it in a
1422 list by specifying the heading that it is under.
1423 \end{notate}
1425 \begin{elisp}
1426 (defun article (name contents &optional heading)
1427 (let ((coordinates (add-nema name
1428 "has content"
1429 contents)))
1430 (when heading (add-nema coordinates "in" heading))
1431 coordinates))
1432 \end{elisp}
1434 \begin{notate}{The `scholium' function} \label{the-scholium-function}
1435 You can use this function to link annotations to objects.
1436 As with the `article' function, you can optionally
1437 categorize the connection on a given list (cf. Note
1438 \ref{the-article-function}).
1439 \end{notate}
1441 \begin{elisp}
1442 (defun scholium (beginning link end &optional heading)
1443 (let ((coordinates (add-nema beginning
1444 link
1445 end)))
1446 (when heading (add-nema coordinates "in" heading))
1447 coordinates))
1448 \end{elisp}
1450 \begin{notate}{Uses of coordinates}
1451 It is convenient to do further immediate processing of the object
1452 we've created while we still have ahold of the coordinates
1453 returned by `add-nema' (e.g., for importing code
1454 that is adjacent to the article, see Note
1455 \ref{import-code-continuations}).
1456 \end{notate}
1458 \begin{notate}{On `get-article'} \label{get-article}
1459 Get the contents of the article named `name'.
1460 We assume that there is only one such article for now.
1461 \end{notate}
1463 \begin{elisp}
1464 ;; Something like this.
1465 (defun get-article (name)
1466 (third (triples-given-beginning-and-middle
1467 name "has content")))
1468 \end{elisp}
1470 \begin{notate}{On `get-names'} \label{get-names}
1471 This function simply gets the names of articles that have
1472 names -- in other words, every triple built around the
1473 ``has content'' relation.
1474 \end{notate}
1476 \begin{elisp}
1477 ;; Something like this.
1478 (defun get-names (&optional heading)
1479 (map first (triples-given-middle "has content")))
1480 \end{elisp}
1482 \section{An example frontend} \label{frontend}
1484 \begin{notate}{Overview of the frontend}
1485 The frontend provides a demonstration of Arxana's functionality that
1486 is directly accessible to the user. Specifically, it is used to
1487 import \LaTeX\ documents into a network structure. They can then be
1488 edited, remixed, saved, browsed, and exported.\todo{Some of this
1489 functionality still needs to be merged in or written!}
1490 \end{notate}
1492 \subsection{Importing \LaTeX\ documents} \label{importing}
1494 \begin{notate}{Importing sketch} \label{importing-sketch}
1495 The code in this section imports a document, represented as a
1496 collection of (sub-)sections and notes. It gathers the sections,
1497 sub-sections, and notes recursively and records their content in a
1498 tree whose nodes are places and whose links express the
1499 ``component-of'' relation described in Note \ref{order-of-order}.
1501 This representation lets us see the geometric, hierarchical, structure
1502 of the document we've imported. It exemplifies a general principle,
1503 that geometric data should be represented by relationships between
1504 places, not direct relationships between strings. This is because
1505 ``the same'' string often appears in ``different'' places in any given
1506 document (e.g. a paper's many sub-sections titled ``Introduction''
1507 will not all have the same content).\todo{Do we need to relax this?}
1509 What goes into the places is in some sense arbitrary. The key is that
1510 whatever is in or attached to these places must tell us
1511 everything we need to know about the part of the document associated
1512 with that place (e.g., in the case of a note, its title and contents).
1513 That's over and above the structural links which say how the
1514 places relate to one another. Finally, all of these places and
1515 structural links will be added to a heading that represents the
1516 document as a whole.
1518 A natural convention we'll use is to put the name of any document
1519 component that's associated with a given place into that place, and
1520 add all other information as annotations.\todo{Does this contradict
1521 what is said above about Introductions?}
1523 Following our usual coding convention, functions are introduced
1524 below ``from the bottom up.''
1525 \end{notate}
1527 \begin{notate}{On `import-code-continuations'} \label{import-code-continuations}
1528 This function runs within the scope of `import-notes'.
1529 In fact, it is meant to run right after a Note itself
1530 has been scanned. The job of this function is to turn the
1531 series of Lisp chunks or other code snippets that follow a given note
1532 into a scholium attached to that note. Each separate snippet becomes
1533 its own annotation. The ``conditional regexps'' used here only work
1534 with Emacs version 23 or higher.\todo{I'm noticing a problem with the
1535 way the `looking-at' form behaves. It matches the expression in
1536 question, but then the match-end is reported as one character less
1537 than it supposed to be. Maybe `looking-at' is just not as good as
1538 `re-search-forward'? But it's what seems easiest to use.}
1539 \end{notate}
1541 \begin{elisp}
1542 ;; coords don't exist anymore, now we use uids
1543 (defun import-code-continuations (coords)
1544 (let ((possible-environments
1545 "\\(1?:lisp\\|idea\\|common\\)"))
1546 (while (looking-at
1547 (concat "\n*?\\\\begin{"
1548 possible-environments
1549 "}"))
1550 (let* ((beg (match-end 0))
1551 (environment (match-string 1))
1552 (end (progn (search-forward-regexp
1553 (concat "\\\\end{"
1554 environment
1555 "}"))
1556 (match-beginning 0)))
1557 (content (buffer-substring-no-properties
1559 end)))
1560 (scholium (scholium coords
1561 "has attachment"
1562 content)
1563 "has type"
1564 environment)))))
1565 \end{elisp}
1567 \begin{notate}{On `import-notes'} \label{import-notes}
1568 We're going to make the daring assumption that the ``textual''
1569 portions of incoming \LaTeX\ documents are contained in ``Notes''.
1570 That assumption is true, at least, for the current document. The
1571 function takes a buffer position `end' that denotes the end of the
1572 current section. The function returns the count of the number of
1573 notes imported, so that `import-within' knows where to start counting
1574 this section's non-note children.\todo{Would this same function work
1575 to import all notes from a buffer without examining its sectioning
1576 structure? Not quite, but close! (Could be a fun exercise to fix
1577 this.)}
1578 \end{notate}
1580 \begin{elisp}
1581 (defun import-notes (end)
1582 (let ((index 0))
1583 (while (re-search-forward (concat "\\\\begin{notate}"
1584 "{\\([^}\n]*\\)}"
1585 "\\( +\\\\label{\\)?"
1586 "\\([^}\n]*\\)?")
1587 end t)
1588 (let* ((name
1589 (match-string-no-properties 1))
1590 (tag (match-string-no-properties 3))
1591 (beg
1592 (progn (next-line 1)
1593 (line-beginning-position)))
1594 (end
1595 (progn (search-forward-regexp
1596 "\\\\end{notate}")
1597 (match-beginning 0)))
1598 ;; this has to change
1599 (coords (add-nema name "in" buffername)))
1600 (setq index (1+ index))
1601 (scholium current-parent
1602 index
1603 coords
1604 buffername)
1605 (scholium coords
1606 "has content"
1607 (buffer-substring-no-properties
1608 beg end))
1609 (import-code-continuations coords)))
1610 index))
1611 \end{elisp}
1613 \begin{notate}{On `import-within'}
1614 Recurse through levels of sectioning in a document to import
1615 \LaTeX\ code. Children that are notes are attached to the
1616 hierarchical structure by the subroutine `import-notes', called by
1617 this function. Sections are attached directly by `import-within'. We
1618 observe that a note ``has content'' whereas a section does not.
1620 Incidentally, when looking for the end of an importing level, `nil' is
1621 an OK result: that describes the case when we have reached the
1622 \emph{last} section at this level \emph{and} there is no subsequent
1623 section at a higher level.
1624 \end{notate}
1626 \begin{elisp}
1627 (defun import-within (levels)
1628 (let ((this-level (car levels))
1629 (next-level (car (cdr levels))) answer)
1630 (while (re-search-forward
1631 (concat
1632 "^\\\\" this-level "{\\([^}\n]*\\)}"
1633 "\\( +\\\\label{\\)?"
1634 "\\([^}\n]*\\)?")
1635 level-end t)
1636 (let* ((name (match-string-no-properties 1))
1637 (at (add-nema name "in" buffername))
1638 (level-end
1639 (or (save-excursion
1640 (search-forward-regexp
1641 (concat "^\\\\" this-level "{.*")
1642 level-end t))
1643 level-end))
1644 (notes-end
1645 (if next-level
1646 (or (progn (point)
1647 (save-excursion
1648 (search-forward-regexp
1649 (concat "^\\\\"
1650 next-level "{.*")
1651 level-end t)))
1652 level-end)
1653 level-end))
1654 (index (let ((current-parent at))
1655 (import-notes notes-end)))
1656 (subsections (let ((current-parent at))
1657 (import-within (cdr levels)))))
1658 (while subsections
1659 (let ((coords (car subsections)))
1660 (setq index (1+ index))
1661 (scholium at
1662 index
1663 coords
1664 buffername)
1665 (setq subsections (cdr subsections))))
1666 (setq answer (cons at answer))))
1667 (reverse answer)))
1668 \end{elisp}
1670 \begin{notate}{On `import-buffer'}
1671 This function import a \LaTeX\ document, taking care of
1672 the high-level, non-recursive, aspects of this operation.
1673 It imports frontmatter (everything up to the first
1674 \verb+\begin{section}+), but assumes ``backmatter'' is
1675 trivial, and does not attempt to import it. The imported
1676 material is classified as a ``document'' with the same
1677 name as the imported buffer.
1679 Links to sections will be made under the ``heading'' of this
1680 document.\todo{The sectioning levels should maybe be scholia attached
1681 to root-coords, but for some reason that wasn't working so well --
1682 investigate later -- maybe it just wasn't good to run after running
1683 `import-within'.}
1684 \end{notate}
1686 \begin{elisp}
1687 (defun import-buffer (&optional buffername)
1688 (save-excursion
1689 (set-buffer (get-buffer (or buffername
1690 (current-buffer))))
1691 (goto-char (point-min))
1692 (search-forward-regexp "\\\\begin{document}")
1693 (search-forward-regexp "\\\\section")
1694 (goto-char (match-beginning 0))
1695 (scholium buffername "is a" "document")
1696 (scholium buffername
1697 "has frontmatter"
1698 (buffer-substring-no-properties
1699 (point-min)
1700 (point))
1701 buffername)
1702 (let* ((root-coords (add-nema buffername "in" buffername))
1703 (levels
1704 '("section" "subsection" "subsubsection"))
1705 (current-parent buffername)
1706 (level-end nil)
1707 (sections (import-within levels))
1708 (index 0))
1709 (while sections
1710 (let ((coords (car sections)))
1711 (setq index (1+ index))
1712 (scholium root-coords
1713 index
1714 coords
1715 buffername))
1716 (setq sections (cdr sections))))))
1717 \end{elisp}
1719 \begin{notate}{On `autoimport-arxana'} \label{autoimport-arxana}
1720 This just calls `import-buffer', and imports this document
1721 into the system.
1722 \end{notate}
1724 \begin{elisp}
1725 (defun autoimport-arxana ()
1726 (interactive)
1727 (import-buffer "arxana-merge.tex"))
1728 \end{elisp}
1730 \subsection{Browsing database contents} \label{browsing}
1732 \begin{notate}{Browsing sketch} \label{browsing-sketch}
1733 This section facilitates browsing of documents represented
1734 with structures like those created in Section
1735 \ref{importing}, and sets the ground for browsing other
1736 sorts of contents (e.g. collections of tasks, as in
1737 Section \ref{managing-tasks}).
1739 In order to facilitate general browsing, it is not enough
1740 to simply use `get-article' (Note \ref{get-article}) and
1741 `get-names' (Note \ref{get-names}), although these
1742 functions provide our defaults. We must provide the means
1743 to find and display different things differently -- for
1744 example, a section's table of contents will typically
1745 be displayed differently from its actual contents.
1747 Indeed, the ability to display and select elements of
1748 document sections (Note \ref{display-section}) is
1749 basically the core browsing deliverable. In the process
1750 we develop a re-usable article selector (Note
1751 \ref{selector}; cf. Note \ref{browsing-tasks}). This in
1752 turn relies on a flexible function for displaying
1753 different kinds of articles (Note \ref{display-article}).
1754 \end{notate}
1756 \begin{notate}{On `display-article'} \label{display-article}
1757 This function takes in the name of the article to display.
1758 Furthermore, it takes optional arguments `retriever' and
1759 `formatter', which tell it how to look up and/or format
1760 the information for display, respectively.
1762 Thus, either we make some statement up front (choosing our
1763 `formatter' based on what we already know about the
1764 article), or we decide what to display after making some
1765 investigation of information attached to the article, some
1766 of which may be retrieved and displayed (this requires
1767 that we specify a suitable `retriever' and a complementary
1768 `formatter').
1770 For example, the major mode in which to display the
1771 article's contents could be stored as a scholium attached
1772 to the article; or we might maintain some information
1773 about ``areas'' of the database that would tell us up
1774 front what which mode is associated with the current area.
1775 (The default is to simply insert the data with no markup
1776 whatsoever.)
1778 Observe that this works when no heading argument is given,
1779 because in that case `get-article' looks for \emph{all}
1780 place pseudonyms. (But of course that won't work well
1781 when we have multiple theories containing things with the
1782 same names, so we should get used to using the heading
1783 argument.)
1785 (The business about requiring the data to be a sequence
1786 before engaging in further formatting is, of course, just
1787 a matter of expediency for making things work with the
1788 current dataset.)
1789 \end{notate}
1791 \begin{elisp}
1792 (defun display-article
1793 (name &optional heading retriever formatter)
1794 (interactive "Mname: ")
1795 (let* ((data (if retriever
1796 (funcall retriever name heading)
1797 (get-article name heading))))
1798 (when (and data (sequencep data))
1799 (save-excursion
1800 (if formatter
1801 (funcall formatter data heading)
1802 (pop-to-buffer (get-buffer-create
1803 "*Arxana Display*"))
1804 (delete-region (point-min) (point-max))
1805 (insert "NAME: " name "\n\n")
1806 (insert data)
1807 (goto-char (point-min)))))))
1808 \end{elisp}
1810 \begin{notate}{An interactive article selector} \label{selector}
1811 The function `get-names' (Note \ref{get-names}) and
1812 similar functions can give us a collection of articles.
1813 The next few functions provide an interactive
1814 functionality for moving through this collection to find
1815 the article we want to look at.
1817 We define a ``display style'' that the article selector
1818 uses to determine how to display various articles. These
1819 display styles are specified by text properties attached
1820 to each option the selector provides. Similarly, when
1821 we're working within a given heading, the relevant heading
1822 is also specified as a text property.
1824 At selection time, these text properties are checked to
1825 determine which information to pass along to
1826 `display-article'.
1827 \end{notate}
1829 \begin{elisp}
1830 (defvar display-style '((nil . (nil nil))))
1832 (defun thing-name-at-point ()
1833 (buffer-substring-no-properties
1834 (line-beginning-position)
1835 (line-end-position)))
1837 (defun get-display-type ()
1838 (get-text-property (line-beginning-position)
1839 'arxana-display-type))
1841 (defun get-relevant-heading ()
1842 (get-text-property (line-beginning-position)
1843 'arxana-relevant-heading))
1845 (defun arxana-list-select ()
1846 (interactive)
1847 (apply 'display-article
1848 (thing-name-at-point)
1849 (get-relevant-heading)
1850 (cdr (assoc (get-display-type)
1851 display-style))))
1853 (define-derived-mode arxana-list-mode fundamental-mode
1854 "arxana-list" "Arxana List Mode.
1856 \\{arxana-list-mode-map}")
1858 (define-key arxana-list-mode-map (kbd "RET")
1859 'arxana-list-select)
1860 \end{elisp}
1862 \begin{notate}{On `pick-a-name'} \label{pick-a-name}
1863 Here `generate' is the name of a function to call to
1864 generate a list of items to display, and `format' is a
1865 function to put these items (including any mark-up) into
1866 the buffer from which individiual items can then be
1867 selected.
1869 One simple way to get a list of names to display would be
1870 to reuse a list that we had already produced (this would
1871 save querying the database each time). We could, in fact,
1872 store a history list of lists of names that had been
1873 displayed previously (cf. Note \ref{local-storage}).
1875 We'll eventually want versions of `generate' that provide
1876 various useful views into the data, e.g., listing all of
1877 the elements of a given section (Note
1878 \ref{display-section}).
1880 Finding all the elements that match a given search term,
1881 whether that's just normal text search or some kind of
1882 structured search would be worthwhile too. Upgrading the
1883 display to e.g. color-code listed elements according to
1884 their type would be another nice feature to add.
1885 \end{notate}
1887 \begin{elisp}
1888 (defun pick-a-name (&optional generate format heading)
1889 (interactive)
1890 (let ((items (if generate
1891 (funcall generate)
1892 (get-names heading))))
1893 (when items
1894 (set-buffer (get-buffer-create "*Arxana Articles*"))
1895 (toggle-read-only -1)
1896 (delete-region (point-min)
1897 (point-max))
1898 (if format
1899 (funcall format items)
1900 (mapc (lambda (item) (insert item "\n")) items))
1901 (toggle-read-only t)
1902 (arxana-list-mode)
1903 (goto-char (point-min))
1904 (pop-to-buffer (get-buffer "*Arxana Articles*")))))
1905 \end{elisp}
1907 \begin{notate}{On `get-section-contents'} \label{get-section-contents}
1908 This function is used by `display-section'
1909 (Note \ref{display-section}) to `pick-a-name' as a generator
1910 for the table of contents of the section with the given
1911 name under the given heading.
1913 This function first finds the triples that begin with the
1914 (placed) name of the section, then checks to see which of
1915 these are in the heading of the document we're examinining
1916 (in other words, which of these links represent structural
1917 information about that document). It also looks at the
1918 items found at the end of these links to see if they are
1919 sections or notes (``noteness'' is determined by them
1920 having content). The links are then sorted by their
1921 middles (which show the order in which these components
1922 have in the section we're examining). After this ordering
1923 information has been used for sorting, it is deleted, and
1924 we're left with just a list of names in the appropriate
1925 order, together with an indication of their noteness.
1926 \end{notate}
1928 \begin{elisp}
1929 (defun get-section-contents (name heading)
1930 (let (contents)
1931 (dolist (triple (triples-given-beginning
1932 `(1 ,(resolve-ambiguity
1933 (get-places name)))))
1934 (when (triple-exact-match
1935 `(2 ,(car triple)) "in" heading)
1936 (let* ((number (print-middle triple))
1937 (site (isolate-end triple))
1938 (noteness
1939 (when (triples-given-beginning-and-middle
1940 site "has content")
1941 t)))
1942 (setq contents
1943 (cons (list number
1944 (print-system-object
1945 (place-contents site))
1946 noteness)
1947 contents)))))
1948 (mapcar 'cdr
1949 (sort contents
1950 (lambda (component1 component2)
1951 (< (parse-integer (car component1))
1952 (parse-integer (car component2))))))))
1953 \end{elisp}
1955 \begin{notate}{On `format-section-contents'} \label{format-section-contents}
1956 A formatter for document contents, used by
1957 `display-document' (Note \ref{display-document}) as input
1958 for `pick-a-name' (Note \ref{pick-a-name}).
1960 Instead of just printing the items one by one,
1961 like the default formatter in `pick-a-name' does,
1962 this version adds appropriate text properties, which
1963 we determine based the second component of
1964 of `items' to format.
1965 \end{notate}
1967 \begin{elisp}
1968 (defun format-section-contents (items heading)
1969 ;; just replicating the default and building on that.
1970 (mapc (lambda (item)
1971 (insert (car item))
1972 (let* ((beg (line-beginning-position))
1973 (end (1+ beg)))
1974 (unless (second item)
1975 (put-text-property beg end
1976 'arxana-display-type
1977 'section))
1978 (put-text-property beg end
1979 'arxana-relevant-heading
1980 heading))
1981 (insert "\n"))
1982 items))
1983 \end{elisp}
1985 \begin{notate}{On `display-section'} \label{display-section}
1986 When browsing a document, if you select a section, you
1987 should display a list of that section's constituent
1988 elements, be they notes or subsections. The question
1989 comes up: when you go to display something, how do you
1990 know whether you're looking at the name of a section, or
1991 the name of an article?
1993 When you get the section's contents out of the database
1994 (Note \ref{get-section-contents})
1995 \end{notate}
1997 \begin{elisp}
1998 (defun display-section (name heading)
1999 (interactive (list (read-string
2000 (concat
2001 "name (default "
2002 (buffer-name) "): ")
2003 nil nil (buffer-name))))
2004 ;; should this pop to the Articles window?
2005 (pick-a-name `(lambda ()
2006 (get-section-contents
2007 ,name ,heading))
2008 `(lambda (items)
2009 (format-section-contents
2010 items ,heading))))
2012 (add-to-list 'display-style
2013 '(section . (display-section
2014 nil)))
2015 \end{elisp}
2017 \begin{notate}{On `display-document'} \label{display-document}
2018 When browsing a document, you should first display its
2019 top-level table of contents. (Most typically, a list of
2020 all of that document's major sections.) In order to do
2021 this, we must find the triples that are begin at the node
2022 representing this document \emph{and} that are in the
2023 heading of this document. This boils down to treating the
2024 document's root as if it was a section and using the
2025 function `display-section' (Note \ref{display-section}).
2026 \end{notate}
2028 \begin{elisp}
2029 (defun display-document (name)
2030 (interactive (list (read-string
2031 (concat
2032 "name (default "
2033 (buffer-name) "): ")
2034 nil nil (buffer-name))))
2035 (display-section name name))
2036 \end{elisp}
2038 \begin{notate}{Work with `heading' argument}
2039 We should make sure that if we know the heading we're
2040 working with (e.g. the name of the document we're
2041 browsing) that this information gets communicated in the
2042 background of the user interaction with the article
2043 selector.
2044 \end{notate}
2046 \begin{notate}{Selecting from a hierarchical display} \label{hierarchical-display}
2047 A fancier ``article selector'' would be able to display
2048 several sections with nice indenting to show their
2049 hierarchical order.
2050 \end{notate}
2052 \begin{notate}{Browser history tricks} \label{history-tricks}
2053 I want to put together (or put back together) something
2054 similar to the multihistoried browser that I had going in
2055 the previous version of Arxana and my Emacs/Lynx-based web
2056 browser, Nero\footnote{{\tt http://metameso.org/~joe/nero.el}}.
2057 The basic features are:
2058 (1) forward, back, and up inside the structure of a given
2059 document; (2) switch between tabs. More advanced features
2060 might include: (3) forward and back globally across all
2061 tabs; (4) explicit understanding of paths that loop.
2063 These sorts of features are independent of the exact
2064 details of what's printed to the screen each time
2065 something is displayed. So, for instance, you could flip
2066 between section manifests a la Note \ref{display-section},
2067 or between hierarchical displays a la Note
2068 \ref{hierarchical-display}, or some combination; the key
2069 thing is just to keep track in some sensible way of
2070 whatever's been displayed!
2071 \end{notate}
2073 \begin{notate}{Local storage for browsing purposes} \label{local-storage}
2074 Right now, in order to browse the contents of the
2075 database, you need to query the database every time. It
2076 might be handy to offer the option to cache names of
2077 things locally, and only sync with the database from time
2078 to time. Indeed, the same principle could apply in
2079 various places; however, it may also be somewhat
2080 complicated to set up. Using two systems for storage, one
2081 local and one permanent, is certainly more heavy-duty than
2082 just using one permanent storage system and the local
2083 temporary display. However, one thing in favor of local
2084 storage systems is that that's what I used in the the
2085 previous prototype of Arxana -- so some code already
2086 exists for local storage! (Caching the list of
2087 \emph{names} we just made a selection from would be one
2088 simple expedient, see Note \ref{pick-a-name}.)
2089 \end{notate}
2091 \begin{notate}{Hang onto absolute references}
2092 Since `get-article' (Note \ref{get-article}) translates
2093 strings into their ``place pseudonyms'', we may want to
2094 hang onto those pseudonyms, because they are, in fact, the
2095 absolute references to the objects we end up working with.
2096 In particular, they should probably go into the
2097 text-property background of the article selector, so it
2098 will know right away what to select!
2099 \end{notate}
2101 \subsection{Exporting \LaTeX\ documents$^*$}
2103 \begin{notate}{Roundtripping}
2104 The easiest test is: can we import a document into the
2105 system and then export it again, and find it unchanged?
2106 \end{notate}
2108 \begin{notate}{Data format}
2109 We should be able to \emph{stably} import and export a
2110 document, as well as export any modifications to the
2111 document that were generated within Arxana. This means
2112 that the exporting functions will have to read the data
2113 format that the importing functions use, \emph{and} that
2114 any functions that edit document contents (or structure)
2115 will also have to use the same format. Furthermore,
2116 \emph{browsing} functions will have to be somewhat aware
2117 of this format. So, this is a good time to ask -- did we
2118 use a good format?
2119 \end{notate}
2121 \subsection{Editing database contents$^*$} \label{editing}
2123 \begin{notate}{Roundtripping, with changes}
2124 Here, we should import a document into the system and then
2125 make some simple changes, and after exporting, check with
2126 diff to make sure the changes are correct.
2127 \end{notate}
2129 \begin{notate}{Re-importing}
2130 One nice feature would be a function to ``re-import'' a
2131 document that has changed outside of the system, and make
2132 changes in the system's version whereever changes appeared
2133 in the source version.
2134 \end{notate}
2136 \begin{notate}{Editing document structure}
2137 The way we have things set up currently, it is one thing
2138 to make a change to a document's textual components, and
2139 another to change its structure. Both types of changes
2140 must, of course, be supported.
2141 \end{notate}
2143 \section{Applications} \label{applications}
2145 \subsection{Managing tasks} \label{managing-tasks}
2147 \begin{notate}{What are tasks?}
2148 Each task tends to have a \emph{name}, a
2149 \emph{description}, a collection of \emph{prerequisite
2150 tasks}, a description of other \emph{material
2151 dependencies}, a \emph{status}, some \emph{justification
2152 of that status}, a \emph{creation date}, and an
2153 \emph{estimated time of completion}. There might actually
2154 be several ``estimated times of completion'', since the
2155 estimate would tend to improve over time. To really
2156 understand a task, one should keep track of revisions like
2157 this.
2158 \end{notate}
2160 \begin{notate}{On `store-task-data'} \label{store-task-data}
2161 Here, we're just filling in a frame. Since ``filling in a
2162 frame'' seems like the sort of operation that might happen
2163 over and over again in different contexts, to save space,
2164 it would probably be nice to have a macro (or similar)
2165 that would do a more general version of what this function
2166 does.
2167 \end{notate}
2169 \begin{elisp}
2170 (defun store-task-data
2171 (name description prereqs materials status
2172 justification submitted eta)
2173 (add-nema name "is a" "task")
2174 (add-nema name "description" description)
2175 (add-nema name "prereqs" prereqs)
2176 (add-nema name "materials" materials)
2177 (add-nema name "status" status)
2178 (add-nema name "status justification" justification)
2179 (add-nema name "date submitted" submitted)
2180 (add-nema name "estimated time of completion" eta))
2181 \end{elisp}
2183 \begin{notate}{On `generate-task-data'} \label{generate-task-data}
2184 This is a simple function to create a new task matching
2185 the description above.
2186 \end{notate}
2188 \begin{elisp}
2189 (defun generate-task-data ()
2190 (interactive)
2191 (let ((name (read-string "Name: "))
2192 (description (read-string "Description: "))
2193 (prereqs (read-string
2194 "Task(s) this task depends on: "))
2195 (materials (read-string "Material dependencies: "))
2196 (status (completing-read
2197 "Status (tabled, in progress, completed):
2198 " '("tabled" "in progress" "completed")))
2199 (justification (read-string "Why this status? "))
2200 (submitted
2201 (read-string
2202 (concat "Date submitted (default "
2203 (substring (current-time-string) 0 10)
2204 "): ")
2205 nil nil (substring (current-time-string) 0 10)))
2206 (eta
2207 (read-string "Estimated date of completion:")))
2208 (store-task-data name description prereqs materials
2209 status
2210 justification submitted eta)))
2211 \end{elisp}
2213 \begin{notate}{Possible enhancements to `generate-task-data'}
2214 In order to make this function very nice, it would be good
2215 to allow ``completing read'' over known tasks when filling
2216 in the prerequisites. Indeed, it might be especially nice
2217 to offer a type of completing read that is similar in some
2218 sense to the tab-completion you get when completing a file
2219 name, i.e., quickly completing certain sub-strings of the
2220 final string (in this case, these substrings would
2221 correspond to task areas we are progressively zooming down
2222 into).
2224 As for the task description, rather than forcing the user
2225 to type the description into the minibuffer, it might be
2226 nice to pop up a separate buffer instead (a la the
2227 Emacs/w3m textarea). If we had a list of all the known
2228 tasks, we could offer completing-read over the names of
2229 existing tasks to generate the list of `prereqs'. It
2230 might be nice to systematize date data, so we could more
2231 easily e.g. sort and display task info ``by date''.
2232 (Perhaps we should be working with predefined database
2233 types for dates and so on.)
2235 Also, before storing the task, it might be nice to offer
2236 the user the chance to review the data they entered.
2237 \end{notate}
2239 \begin{notate}{On `get-filler'} \label{get-filler}
2240 Just a wrapper for `triples-given-beginning-and-middle'.
2241 (Maybe we should add `heading' as an optional argument here.)
2242 \end{notate}
2244 \begin{elisp}
2245 (defun get-filler (frame slot)
2246 (third (first
2247 (print-triples
2248 (triples-given-beginning-and-middle frame
2249 slot)))))
2250 \end{elisp}
2252 \begin{notate}{On `get-task'} \label{get-task}
2253 Uses `get-filler' (Note \ref{get-filler}) to assemble the
2254 elements of a task's frame.
2255 \end{notate}
2257 \begin{elisp}
2258 (defun get-task (name)
2259 (when (triple-exact-match name "is a" "task")
2260 (list (get-filler name "description")
2261 (get-filler name "prereqs")
2262 (get-filler name "materials")
2263 (get-filler name "status")
2264 (get-filler name "status justification")
2265 (get-filler name "date submitted")
2266 (get-filler name
2267 "estimated time of completion"))))
2268 \end{elisp}
2270 \begin{notate}{On `review-task'} \label{review-task}
2271 This is a function to review a task by name.
2272 \end{notate}
2274 \begin{elisp}
2275 (defun review-task (name)
2276 (interactive "MName: ")
2277 (let ((task-data (get-task name)))
2278 (if task-data
2279 (display-task task-data)
2280 (message "No data."))))
2282 (defun display-task (data)
2283 (save-excursion
2284 (pop-to-buffer (get-buffer-create
2285 "*Arxana Display*"))
2286 (delete-region (point-min) (point-max))
2287 (insert "NAME: " name "\n\n")
2288 (insert "DESCRIPTION: " (first data) "\n\n")
2289 (insert "TASKS THIS TASK DEPENDS ON: "
2290 (second data) "\n\n")
2291 (insert "MATERIAL DEPENDENCIES: "
2292 (third data) "\n\n")
2293 (insert "STATUS: " (fourth data) "\n\n")
2294 (insert "WHY THIS STATUS?: " (fifth data) "\n\n")
2295 (insert "DATE SUBMITTED:" (sixth data) "\n\n")
2296 (insert "ESTIMATED TIME OF COMPLETION: "
2297 (seventh data) "\n\n")
2298 (goto-char (point-min))
2299 (fill-individual-paragraphs (point-min) (point-max))))
2300 \end{elisp}
2302 \begin{notate}{Possible enhancements to `review-task'}
2303 Breaking this down into a function to select the task and
2304 another function to display the task would be nice. Maybe
2305 we should have a generic function for selecting any object
2306 ``by name'', and then special-purpose functions for
2307 displaying objects with different properties.
2309 Using text properties, we could set up a ``field-editing
2310 mode'' that would enable you to select a particular field
2311 and edit it independently of the others. Another more
2312 complex editing mode would \emph{know} which fields the
2313 user had edited, and would store all edits back to the
2314 database properly. See Section \ref{editing} for more on
2315 editing.
2316 \end{notate}
2318 \begin{notate}{Browsing tasks} \label{browsing-tasks}
2319 The function `pick-a-name' (Note \ref{pick-a-name}) takes
2320 two functions, one that finds the names to choose from,
2321 and the other that says how to present these names. We
2322 can therefore build `pick-a-task' on top of `pick-a-name'.
2323 \end{notate}
2325 \begin{elisp}
2326 (defun get-tasks ()
2327 (mapcar #'first
2328 (print-triples
2329 (triples-given-middle-and-end "is a" "task")
2330 t)))
2332 (defun pick-a-task ()
2333 (interactive)
2334 (pick-a-name
2335 'get-tasks
2336 (lambda (items)
2337 (mapc (lambda (item)
2338 (let ((pos (line-beginning-position)))
2339 (insert item)
2340 (put-text-property pos (1+ pos)
2341 'arxana-display-type
2342 'task)
2343 (insert "\n"))) items))))
2345 (add-to-list 'display-style
2346 '(task . (get-task display-task)))
2347 \end{elisp}
2349 \begin{notate}{Working with theories}
2350 Presumably, like other related functions, `get-tasks'
2351 should take a heading argument.
2352 \end{notate}
2354 \begin{notate}{Check display style}
2355 Check if this works, and make style consistent between
2356 this usage and earlier usage.
2357 \end{notate}
2359 \begin{notate}{Example tasks}
2360 It might be fun to add some tasks associated with
2361 improving Arxana, just to show that it can be done...
2362 maybe along with a small importer to show how importing
2363 something without a whole lot of structure can be easy.
2364 \end{notate}
2366 \begin{notate}{Org mode integration}
2367 The ``default'' task manager on Emacs is Org mode. It would be good
2368 to provide integration between Org mode and Arxana. This is one of
2369 the first things that Emacs people ask about when they hear about
2370 Arxana.
2371 \end{notate}
2373 \subsection{``Modelling mathematics the way it is really done''} \label{farm-demo}
2375 \begin{notate}{A demonstration}
2376 In a paper for the 5th ACM SIGPLAN International Workshop on
2377 Functional Art, Music, Modelling and Design (FARM 2017), we talk about
2378 how Arxana can be applied to model mathematical proofs. A rather
2379 advanced form of ``mathematical knowledge management'' is proposed,
2380 that integrates dialogue, heuristics, and that ultimately moves in the
2381 direction of an AI system. In this section, we should walk through
2382 this application.
2383 \end{notate}
2385 \section{Conclusion} \label{conclusion}
2387 \begin{notate}{Ending and beginning again}
2388 This is the end of this Arxana demo system. Contributions that
2389 support the development of the Arxana project are welcome.
2390 \end{notate}
2392 \appendix
2394 \section{Appendix: A simple literate programming system} \label{appendix-lit}
2396 \begin{notate}{The literate programming system used in this paper}
2397 This code defines functions that grab all the Lisp portions of this
2398 document, and evaluates the Emacs Lisp sections. It requires that the
2399 \LaTeX\ be written in a certain consistent way. The function assumes
2400 that this document is the current buffer.
2402 \begin{verbatim}
2403 (defvar lit-code-beginning-regexp
2404 "^\\\\begin{elisp}")
2406 (defvar lit-code-end-regexp
2407 "^\\\\end{elisp}")
2409 (defvar lit-count 0)
2411 (defun lit-eval ()
2412 (interactive)
2413 (lit-process 'eval))
2415 (defun lit-process (&optional code)
2416 (interactive)
2417 (setq lit-count (1+ lit-count))
2418 (save-excursion
2419 (let ((to-buffer (concat "*Lit Code " (int-to-string
2420 lit-count)"*"))
2421 (from-buffer (buffer-name (current-buffer))))
2422 (set-buffer (get-buffer-create to-buffer))
2423 (erase-buffer)
2424 (set-buffer (get-buffer-create from-buffer))
2425 (goto-char (point-min))
2426 (while (re-search-forward
2427 lit-code-beginning-regexp nil t)
2428 (let* ((beg (match-end 0))
2429 (end (save-excursion
2430 (search-forward-regexp
2431 lit-code-end-regexp nil t)
2432 (match-beginning 0)))
2433 (match (buffer-substring beg end)))
2434 (save-excursion
2435 (set-buffer to-buffer)
2436 (insert match))))
2437 (case code
2438 ('eval
2439 (set-buffer to-buffer)
2440 (eval-buffer)
2441 ;; (kill-buffer (current-buffer))
2444 (switch-to-buffer to-buffer))))))
2445 \end{verbatim}
2446 \end{notate}
2448 \begin{notate}{A literate style}
2449 Ideally, each function will have its own Note to introduce
2450 it, and will not be called before it has been defined. I
2451 sometimes make an exception to this rule, for example,
2452 functions used to form recursions may appear with no
2453 further introduction, and may be called before they are
2454 defined.
2455 \end{notate}
2457 \end{document}