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