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.
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.
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/>.
18 %% (find-file "~/arxana/latex/arxana-merge.tex")
20 %% (goto-char (point-max))
21 %% (let ((beg (progn (search-backward "\\begin{verbatim}")
23 %% (end (progn (search-forward "\\end{verbatim}")
24 %% (match-beginning 0))))
25 %% (eval-region beg end)
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!
45 \documentclass{article
}
47 \usepackage[T1]{fontenc}
55 \newcommand{\todo}[1]{\footnote{\bf TODO:
#1}}
56 \newcommand{\meta}[1]{$
\langle$
{\it #1}$
\rangle$
}
58 \theoremstyle{definition
}
59 \newtheorem{nota
}{Note
}[section
]
63 \newenvironment{notate
}[1]
64 {\begin{nota
}[{\bf {\em #1}}]}%
68 \newenvironment{elisp
}
69 {\let\ORGverbatim@font
\verbatim@font
70 \def\verbatim@font
{\ttfamily\scshape}%
73 \let\verbatim@font
\ORGverbatim@font
}
78 {\let\ORGverbatim@font
\verbatim@font
79 \def\verbatim@font
{\ttfamily\slshape}%
82 \let\verbatim@font
\ORGverbatim@font
}
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}
97 \abstract{A tool for building hackable semantic hypertext platforms.
98 An overview and link to our source code repository is at
{\tt
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.
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
}.)
131 (let ((beg (search-forward "\
\begin{verbatim
}"))
132 (end (progn (search-forward "\
\end{verbatim
}")
133 (match-beginning
0))))
134 (eval-region beg end)
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
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...)
}
163 \subsection{Preliminaries
}
165 \begin{notate
}{Some preliminaries
}
166 We define several simple preliminary functions that we use later on.
169 \begin{notate
}{Required packages
}
170 We use the Common Lisp compatibility functions.
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.
183 (defun filter (pred stuff)
185 (dolist (item stuff (reverse ans))
186 (if (funcall pred item)
187 (setq ans (cons item ans))
192 (filter '(lambda (x) (= (
% x 2) 1)) '(1 2 3 4 5 6 7))
196 \begin{notate
}{On `intersection'
}
197 Set-theoretic intersection operation.
198 More general than the version coming from the `cl' package
202 (defun intersection (&rest arg)
203 (cond ((null arg) nil)
204 ((null (cdr arg)) (car arg))
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))))))))))
216 (intersection '(a b c d e f g h j)
222 \begin{notate
}{On `mapply'
}
223 Map and apply rolled into one.
227 (if (member nil l) nil
228 (cons (apply f (mapcar 'car l))
229 (mapply f (mapcar 'cdr l)))))
233 (mapply '+ '((
1 2) (
3 4)))
237 \begin{notate
}{On `sublis'
}
238 Substitute objects in a list.
242 (defun sublis (sub lis)
245 ((assoc lis sub) (cadr (assoc lis sub)))
247 (t (cons (sublis sub (car lis))
248 (sublis sub (cdr lis))))))
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).
}
269 "Create a new plexus."
270 (let ((newbie (list '*plexus*
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
290 '(,(+ (car plexus-registry)
1)
291 (,(car plexus-registry) ,newbie))))
295 \begin{notate
}{The ``remove-plexus'' function
}
296 When we're done with our plexus, we should tidy up after ourselves.
300 (defun remove-plexus (plex)
302 ;; Wipe out the hash tables
304 (clrhash (nth (+ i
2) plex)))
305 ;; Remove the entry from the registry.
306 (setq plexus-registry
308 (car plexus-registry)
311 (cdr plexus-registry))
312 (cdr plexus-registry)))))
316 (defun show-plexus-registry ()
320 \begin{notate
}{HONEY set up
}
321 These variables are needed for a coherent set-up.
\todo{Explain
326 (defvar plexus-registry '(
0 nil))
327 (defvar current-plexus nil)
330 \begin{notate
}{Network selection
}
331 We can work with several networks, only one of
332 which is ``current'' at any given time.
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."
348 \begin{notate
}{On `next-unique-id'
}
349 Increment the identifier that tells us how many nemas are in our network.
353 (defun next-unique-id ()
354 "Produce a yet unused unique identifier."
355 (
1+ (cadr current-plexus)))
358 \begin{notate
}{On `reset-plexus'
}
359 Reset article counter and hash tables. Redefine ``ground'' and
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)
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))
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?
}
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.
400 (nth
2 current-plexus))
401 ;; Add record to list of forward links of source.
404 (gethash src (nth
3 current-plexus) nil))
405 (nth
3 current-plexus))
406 ;; Add record to list of backward links of sink.
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.
418 \begin{notate
}{Retrieving elements of a nema
}
419 These functions exist to get the relevant components
420 of a nema, given its uid.
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))))
437 \begin{notate
}{On `update-content'
}
444 (defun update-content (uid txt)
445 "Replace the content of the nema."
447 (let ((x (gethash uid (nth
2 current-plexus))))
448 `(,(car x) ; old source
449 ,(cadr x) . ; old sink
451 (nth
2 current-plexus)))
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
464 Update the entry in the backward link table.
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.
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)
483 (nth
3 current-plexus)
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.
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.
496 (cons `(,uid . ,new-src)
497 (delete `(,uid . ,old-src)
499 (nth
4 current-plexus)
501 (nth
4 current-plexus))))
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
514 Update the entry in the forward link table.
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.
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)
533 (nth
4 current-plexus)
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.
541 (cons `(,uid . ,old-src)
543 (nth
4 current-plexus)
545 (nth
4 current-plexus))
546 ;; Update the entry in the forward link table.
548 (cons `(,uid . ,new-snk)
549 (delete `(,uid . ,old-snk)
551 (nth
3 current-plexus)
553 (nth
3 current-plexus))))
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.
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)))))
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)))))
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))))
583 \begin{notate
}{Functions for gathering links
}
584 Links are stored on triples alongside other
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))))
598 \begin{notate
}{On `label-nema'
}
599 Nemas can be given a unique human-readable label in addition
600 to their numeric uid.
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)))
610 \begin{notate
}{Label to uid and uid to label lookup
}
611 These functions allow the exchange of uid and label.
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))
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.
632 (defun download-en-masse ()
633 "Produce a representation of the database as quintuples."
635 (maphash (lambda (uid tplt)
637 (let ((src (car tplt))
639 (txt (nthcdr
2 tplt)))
640 ; Obtain next label if exists.
641 (setq lbl (gethash uid
642 (nth
5 current-plexus)
644 ; Write data to list.
645 (setq plex (cons `(,uid ,lbl ,src ,snk . ,txt)
647 (nth
2 current-plexus))
648 ; Return list of data.
652 \begin{notate
}{On `upload-en-masse'
}
653 Unpack quintuplets. Plug into tables.
654 Bump up article counter as needed.
658 (defun upload-en-masse (plex)
659 "Load a representation of a database as quintuples into memory."
660 (dolist (qplt plex t)
662 (let ((uid (car qplt))
666 (txt (nthcdr
4 qplt)))
670 (nth
2 current-plexus))
673 (gethash src (nth
3 current-plexus) nil))
674 (nth
3 current-plexus))
678 (gethash snk (nth
4 current-plexus) nil))
679 (nth
4 current-plexus))
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)))))
689 \begin{notate
}{On `add-en-masse'
}
690 Given several articles, add all of them at once.
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)
701 (label-nema uid (car qplt))))
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.
713 \begin{notate
}{Various lookup functions
}
714 These functions allow testing and lookup of various elements
720 "Is this a valid uid?"
722 (not (eq z (gethash uid (nth
2 current-plexus) z)))))
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?"
733 (defun source-p (x y)
734 "Is the former nema the sink of the latter?"
735 (equal x (get-source 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)))
747 "Does nema x link to nema y?"
748 (when (member x (mapcar
750 (get-backward-links y)))
753 (defun triple-p (x y z)
754 "Do the three items form a triplet?"
759 "Is this object a plexus?"
762 (equal (car x) "*plexus*")))
766 (setq ans (and ans (hash-table-p
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.
}
780 (defmacro do-plexus (var res body)
781 `((maphash (lambda (,var val) ,body)
782 (nth
2 current-plexus))
785 ;; This maps over the keys; func should be
786 ;; defined appropriately.
787 (defun map-plexus (func)
791 (push (apply func (list key)) ans))
792 (nth
2 current-plexus))
795 (defun filter-plexus (pred)
799 (when (apply pred (list val))
801 (nth
2 current-plexus))
805 \begin{notate
}{Filtering convenience functions
}
806 Several convenience functions for filtering the plexus can be
807 defined.
\todo{Note: `links-from' might sort of work
808 instead of `triples-given-beginning-and-end', but it
809 returns uids rather than triples - to discuss!
810 A more general concern are we meant to return the
811 triple's content or its uid, or both?
}
815 (defun triples-given-beginning (node)
816 "Get triples outbound from the given NODE."
817 (remove nil (map-plexus
818 (lambda (x) (when (equal (get-source x)
824 (defun triples-given-end (node)
825 "Get triples inbound into NODE."
826 (remove nil (map-plexus
827 (lambda (x) (when (equal (get-sink x)
833 (defun triples-given-middle (edge)
834 "Get the triples that run along EDGE."
835 (remove nil (map-plexus
836 (lambda (x) (when (equal (get-content x)
842 (defun triples-given-middle-and-end (edge node)
843 "Get the triples that run along EDGE into NODE."
844 (remove nil (map-plexus
845 (lambda (x) (when (and
846 (equal (get-content x)
854 (defun triples-given-beginning-and-middle (node edge)
855 "Get the triples that run from NODE along EDGE."
856 (remove nil (map-plexus
857 (lambda (x) (when (and
858 (equal (get-source x)
860 (equal (get-content x)
866 (defun triples-given-beginning-and-end (node1 node2)
867 "Get the triples that run from NODE1 to NODE2."
868 (remove nil (map-plexus
869 (lambda (x) (when (and
870 (equal (get-source x)
878 (defun triple-exact-match (node1 edge node2)
879 "Get the triples that run from NODE1 along EDGE to
881 (remove nil (map-plexus
882 (lambda (x) (when (and
883 (equal (get-source x)
885 (equal (get-content x)
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'?
}
902 (car (nth
0 (cdr (assoc n (cdr article-list))))))
905 (cdr (nth
0 (cdr (assoc n (cdr article-list))))))
908 (nth
1 (cdr (assoc n (cdr article-list)))))
911 (car (nth
2 (cdr (assoc n (cdr article-list))))))
914 (cdr (nth
2 (cdr (assoc n (cdr article-list))))))
917 (mapcar (quote car) (cdr article-list)))
919 (defun get-gnd nil
0)
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
}?
}
931 (defmacro search-cond (vars prop)
932 "Find all n-tuplets satisfying a condition"
933 (let ((foo '(lambda (vars cmnd)
936 `(dolist (,(car vars) uids)
937 ,(funcall foo (cdr vars) cmnd))
939 (funcall foo vars prop)))
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.
956 (((a blue big) (b funny) (c green small)
957 ((b src a) (b snk c) (c src a))
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:
970 ((c (green small) ((b snk c) (c src a)))
971 (b (funny) ((b src a)))
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:
983 ((c (green small) ((b snk c) (c src a)))
984 (b (funny) ((b src a)))
988 (intersection (list (get-snk b)) (get-forward-links a)))
990 (intersection (get-backward-links a)))
993 This is done using the function `tplts2cmd', e.g.
995 (tplts2cmd 'c '((b snk c) (c src a)))
997 (intersection (list (get-snk b)) (get-forward-links a))
999 Subsequently, we filter over the predicates:
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)))))
1007 This is done with the command `add-filt':
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))))
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:
1025 (when (and (blue (get-content node))
1026 (big (get-content node)))
1027 (setq ans (cons node ans))))
1030 This is done by invoking `first2cmd':
1032 (first2cmd '(blue big))
1036 (when (and (blue (get-content node))
1037 (big (get-content node)))
1038 (setq ans (cons node ans))))
1041 And putting this all together:
1044 '((c (green small) ((b snk c) (c src a)))
1045 (b (funny) ((b src a)))
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))))
1055 (when (and (blue (get-content node))
1056 (big (get-content node)))
1057 (setq ans (cons node ans))))
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:
1074 This combination of operations is combined into the `search'
1075 function, which can be called as follows:
1086 Having described what the functions are supposed to do and how
1087 they work together, we now proceed to implement them.
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.
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))|.
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.
1120 (defun scheduler (new-nodes links sched)
1121 (if (null new-nodes)
1123 (let ((remaining-nodes new-nodes)
1126 (old-nodes (mapcar 'car sched)))
1128 (setq candidate (car remaining-nodes))
1129 (setq remaining-nodes (cdr remaining-nodes))
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)))))
1138 (scheduler (remove candidate new-nodes)
1140 (cons (list (car candidate)
1146 \begin{notate
}{On `tplts2cmd'
}
1147 \ldots\todo{Explain.
}
1151 (defun tplts2cmd (var tplts)
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))))
1171 \begin{notate
}{On `add-filt'
}
1172 \ldots\todo{Explain.
}
1176 (defun add-filt (var preds tplts)
1184 (list 'get-txt var)))
1186 ,(tplts2cmd var tplts))))
1189 \begin{notate
}{On `first2cmd'
}
1190 \ldots\todo{Explain.
}
1194 (defun first2cmd (preds)
1196 (dolist (node (get-ids) ans)
1201 (cons pred '((get-txt node))))
1203 (setq ans (cons node ans))))))
1206 \begin{notate
}{On `query2cmd'
}
1207 \ldots\todo{Explain.
}
1211 (defun query2cmd (query)
1212 (let ((backwards (reverse query)))
1215 (list (caar backwards)
1216 (first2cmd (cdar backwards)))
1219 (add-filt (first x) (second x) (third x)))
1220 (cdr backwards))))))
1223 \begin{notate
}{On `matcher'
}
1224 \ldots\todo{Explain.
}
1228 (defun matcher (assgmt reqmts)
1229 (if (null reqmts) (list assgmt)
1233 (matcher (cons (list (caar reqmts) x)
1236 (apply 'intersection
1239 (cdar reqmts)))))))))
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:
1248 (matcher '((x
1)) '((y (list
1 3))
1249 (z (list (+ x y) (- y x)))))
1251 (((z
2) (y
1) (x
1))
1254 ((z
2) (y
3) (x
1)))
1256 (matcher nil '((x (list
1))
1258 (z (list (+ x y) (- y x)))))
1260 (((z
2) (y
1) (x
1))
1263 ((z
2) (y
3) (x
1)))
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
}?
}
1272 (defun search (query)
1279 (list (caar query)))))))
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.
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.
1296 (defun node-fun (node get-code get-links)
1297 (let ((code (funcall get-code node))
1298 (links (funcall get-links node)))
1307 (mapcar #'(lambda (x)
1314 (mapcar #'(lambda (x)
1315 (if (fboundp (car x))
1317 ',(symbol-function (car x)))
1318 `(fmakunbound ',(car x))))
1322 \begin{notate
}{On `tangle-module'
}
1323 Recursively replace the chunks to recover executable code.
\todo{Explain.
}
1327 (defun tangle-module (node get-cont ins-links)
1329 (funcall get-cont node)
1330 (mapcar #'(lambda (x)
1332 (tangle-module (cdr x)
1335 (funcall ins-links node))))
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.
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)))))
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
}?
}
1359 (defun set-src (n x)
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))))
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)
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))))
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)
1394 (cdr article-list)))
1396 (nth
3 (assoc snk (cdr article-list)))))
1397 (setcdr backlink (cons (car article-list)
1400 (nth
1 (assoc src (cdr article-list)))))
1401 (setcdr backlink (cons (car article-list)
1403 (- (setcar article-list (+
1 (car article-list)))
1)))
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))))
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)))
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
}).
1434 (set-current-plexus (add-plexus))
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
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.)
1469 (defun article (name contents &optional heading)
1470 (let ((coordinates (add-nema name
1473 (when heading (add-nema coordinates "in" heading))
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
}).
1485 (defun scholium (beginning link end &optional heading)
1486 (let ((coordinates (add-nema beginning
1489 (when heading (add-nema coordinates "in" heading))
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
}).
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.
1507 ;; Something like this.
1508 (defun get-article (name)
1509 (third (triples-given-beginning-and-end
1510 name "arxana-merge.tex")))
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?
}
1521 (defun get-names (&optional heading)
1522 (mapcar #'get-source
1523 (mapcar #'first (triples-given-middle "has content"))))
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!
}
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.''
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?
}
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\\)"))
1588 (concat "
\n*?\\\
\begin{"
1589 possible-environments
1591 (let* ((beg (match-end
0))
1592 (environment (match-string
1))
1593 (end (progn (search-forward-regexp
1597 (match-beginning
0)))
1598 (content (buffer-substring-no-properties
1601 (scholium (scholium coords
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
1622 (defun import-notes (end)
1624 (while (re-search-forward (concat "\\\
\begin{notate
}"
1626 "\\( +\\\
\label{\\)?"
1630 (match-string-no-properties
1))
1631 (tag (match-string-no-properties
3))
1633 (progn (next-line
1)
1634 (line-beginning-position)))
1636 (progn (search-forward-regexp
1638 (match-beginning
0)))
1639 ;; get the uid for our new nema
1640 (coords (add-nema name "in" buffername)))
1643 (buffer-substring-no-properties
1645 (setq index (
1+ index))
1646 ;; current-parent is in scope inside import-within
1647 (scholium current-parent
1651 (import-code-continuations coords)))
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.
1669 (defun import-within (levels)
1670 (let ((this-level (car levels))
1671 (next-level (car (cdr levels))) answer)
1672 (while (re-search-forward
1674 "^\\\\" this-level "
{\\(
[^
}\n]*\\)
}"
1675 "\\( +\\\
\label{\\)?"
1678 (let* ((name (match-string-no-properties
1))
1679 (at (add-nema name "in" buffername))
1682 (search-forward-regexp
1683 (concat "^\\\\" this-level "
{.*")
1690 (search-forward-regexp
1696 (index (let ((current-parent at))
1697 (import-notes notes-end)))
1698 (subsections (let ((current-parent at))
1699 (import-within (cdr levels)))))
1701 (let ((coords (car subsections)))
1702 (setq index (
1+ index))
1707 (setq subsections (cdr subsections))))
1708 (setq answer (cons at answer))))
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
1729 (defun import-buffer (&optional buffername)
1731 (set-buffer (get-buffer (or buffername
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
1740 (buffer-substring-no-properties
1744 (let* ((root-coords (add-nema buffername "in" buffername))
1746 '("section" "subsection" "subsubsection"))
1747 (current-parent buffername)
1749 (sections (import-within levels))
1752 (let ((coords (car sections)))
1753 (setq index (
1+ index))
1754 (scholium root-coords
1758 (setq sections (cdr sections))))))
1761 \begin{notate
}{On `autoimport-arxana'
} \label{autoimport-arxana
}
1762 This just calls `import-buffer', and imports this
document
1767 (defun autoimport-arxana ()
1769 (import-buffer "arxana-merge.tex"))
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
}).
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
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
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
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
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))))
1840 (when (and data (sequencep data))
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")
1849 (goto-char (point-min)))))))
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
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 ()
1889 (apply 'display-article
1890 (thing-name-at-point)
1891 (get-relevant-heading)
1892 (cdr (assoc (get-display-type)
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)
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
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.
1930 (defun pick-a-name (&optional generate format heading)
1932 (let ((items (if generate
1934 (get-names heading))))
1936 (set-buffer (get-buffer-create "*Arxana Articles*"))
1937 (toggle-read-only -
1)
1938 (delete-region (point-min)
1941 (funcall format items)
1942 (mapc (lambda (item) (insert item "
\n")) items))
1943 (toggle-read-only t)
1945 (goto-char (point-min))
1946 (pop-to-buffer (get-buffer "*Arxana Articles*")))))
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.
1971 (defun get-section-contents (name heading)
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))
1981 (when (triples-given-beginning-and-middle
1986 (print-system-object
1987 (place-contents site))
1992 (lambda (component1 component2)
1993 (< (parse-integer (car component1))
1994 (parse-integer (car component2))))))))
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.
2010 (defun format-section-contents (items heading)
2011 ;; just replicating the default and building on that.
2012 (mapc (lambda (item)
2014 (let* ((beg (line-beginning-position))
2016 (unless (second item)
2017 (put-text-property beg end
2018 'arxana-display-type
2020 (put-text-property beg end
2021 'arxana-relevant-heading
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
})
2040 (defun display-section (name heading)
2041 (interactive (list (read-string
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
2051 (format-section-contents
2054 (add-to-list 'display-style
2055 '(section . (display-section
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
}).
2071 (defun display-
document (name)
2072 (interactive (list (read-string
2075 (buffer-name) "): ")
2076 nil nil (buffer-name))))
2077 (display-section name name))
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
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
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!
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
}.)
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!
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?
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
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.
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.
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.
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
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
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))
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.
2231 (defun generate-task-data ()
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? "))
2244 (concat "Date submitted (default "
2245 (substring (current-time-string)
0 10)
2247 nil nil (substring (current-time-string)
0 10)))
2249 (read-string "Estimated date of completion:")))
2250 (store-task-data name description prereqs materials
2252 justification submitted eta)))
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
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.
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.)
2287 (defun get-filler (frame slot)
2290 (triples-given-beginning-and-middle frame
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.
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")
2309 "estimated time of completion"))))
2312 \begin{notate
}{On `review-task'
} \label{review-task
}
2313 This is a function to review a task by name.
2317 (defun review-task (name)
2318 (interactive "MName: ")
2319 (let ((task-data (get-task name)))
2321 (display-task task-data)
2322 (message "No data."))))
2324 (defun display-task (data)
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))))
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
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'.
2371 (triples-given-middle-and-end "is a" "task")
2374 (defun pick-a-task ()
2379 (mapc (lambda (item)
2380 (let ((pos (line-beginning-position)))
2382 (put-text-property pos (
1+ pos)
2383 'arxana-display-type
2385 (insert "
\n"))) items))))
2387 (add-to-list 'display-style
2388 '(task . (get-task display-task)))
2391 \begin{notate
}{Working with theories
}
2392 Presumably, like other related functions, `get-tasks'
2393 should take a heading argument.
2396 \begin{notate
}{Check display style
}
2397 Check if this works, and make style consistent between
2398 this usage and earlier usage.
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.
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
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
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.
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.
2445 (defvar lit-code-beginning-regexp
2446 "^\\\
\begin{elisp
}")
2448 (defvar lit-code-end-regexp
2451 (defvar lit-count
0)
2455 (lit-process 'eval))
2457 (defun lit-process (&optional code)
2459 (setq lit-count (
1+ lit-count))
2461 (let ((to-buffer (concat "*Lit Code " (int-to-string
2463 (from-buffer (buffer-name (current-buffer))))
2464 (set-buffer (get-buffer-create to-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)))
2477 (set-buffer to-buffer)
2481 (set-buffer to-buffer)
2483 ;; (kill-buffer (current-buffer))
2486 (switch-to-buffer to-buffer))))))
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