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 ;; Return the id of the new nema.
416 \begin{notate
}{Retrieving elements of a nema
}
417 These functions exist to get the relevant components
418 of a nema, given its uid.
422 (defun get-content (uid)
423 "Return the content of the nema."
424 (cddr (gethash uid (nth
2 current-plexus))))
426 (defun get-source (uid)
427 "Return the source of the nema."
428 (car (gethash uid (nth
2 current-plexus))))
430 (defun get-sink (uid)
431 "Return the sink of the nema."
432 (cadr (gethash uid (nth
2 current-plexus))))
435 \begin{notate
}{On `update-content'
}
442 (defun update-content (uid txt)
443 "Replace the content of the nema."
445 (let ((x (gethash uid (nth
2 current-plexus))))
446 `(,(car x) ; old source
447 ,(cadr x) . ; old sink
449 (nth
2 current-plexus)))
452 \begin{notate
}{On `update-source'
}
453 Extract current source.
454 Extract current sink.
455 Extract current content.
456 Update the entry in the article table.
457 Remove the entry with the old source in the
458 forward link table. If that is the only entry
459 filed under old-src, remove it from table.
460 Add an entry with the new source in the
462 Update the entry in the backward link table.
466 (defun update-source (uid new-src)
467 "Replace the source of the nema."
468 (let* ((x (gethash uid (nth
2 current-plexus)))
469 (old-src (car x)) ; extract current source
470 (old-snk (cadr x)) ; extract current sink
471 (old-txt (cddr x))) ; extract current content
472 ;; Update the entry in the nema table.
474 `(,new-src ,old-snk . ,old-txt)
475 (nth
2 current-plexus))
476 ;; Remove the entry with the old source in the
477 ;; forward link table. If that is the only entry
478 ;; filed under old-src, remove it from table.
479 (let ((y (delete `(,uid . ,old-snk)
481 (nth
3 current-plexus)
484 (puthash old-src y (nth
3 current-plexus))
485 (remhash old-src (nth
3 current-plexus))))
486 ;; Add an entry with the new source in the
487 ;; forward link table.
489 (cons `(,uid . ,old-snk)
490 (gethash old-src (nth
3 current-plexus) nil))
491 (nth
3 current-plexus))
492 ;; Update the entry in the backward link table.
494 (cons `(,uid . ,new-src)
495 (delete `(,uid . ,old-src)
497 (nth
4 current-plexus)
499 (nth
4 current-plexus))))
502 \begin{notate
}{On `update-sink'
} \label{update-sink
}
503 Extract current source.
504 Extract current sink.
505 Extract current content.
506 Update the entry in the article table.
507 Remove the entry with the old sink in the
508 backward link table. If that is the only entry
509 filed under old-src, remove it from table.
510 Add an entry with the new source in the
512 Update the entry in the forward link table.
516 (defun update-sink (uid new-snk)
517 "Change the sink of the nema."
518 (let* ((x (gethash uid (nth
2 current-plexus)))
519 (old-src (car x)) ; extract current source
520 (old-snk (cadr x)) ; extract current sink
521 (old-txt (cddr x))) ; extract current content
522 ; Update the entry in the nema table.
524 `(,old-src ,new-snk . ,old-txt)
525 (nth
2 current-plexus))
526 ;; Remove the entry with the old sink in the
527 ;; backward link table. If that is the only entry
528 ;; filed under old-src, remove it from table.
529 (let ((y (delete `(,uid . ,old-src)
531 (nth
4 current-plexus)
534 (puthash old-snk y (nth
4 current-plexus))
535 (remhash old-snk (nth
4 current-plexus))))
536 ;; Add an entry with the new source in the
537 ;; backward link table.
539 (cons `(,uid . ,old-src)
541 (nth
4 current-plexus)
543 (nth
4 current-plexus))
544 ;; Update the entry in the forward link table.
546 (cons `(,uid . ,new-snk)
547 (delete `(,uid . ,old-snk)
549 (nth
3 current-plexus)
551 (nth
3 current-plexus))))
554 \begin{notate
}{On `remove-nema'
}
555 Remove forward link created by article.
556 Remove backward link created by article.
557 Remove record from article table.
561 (defun remove-nema (uid)
562 "Remove this nema from the database."
563 (let ((old-src (car (gethash uid (nth
2 current-plexus))))
564 (old-snk (cadr (gethash uid (nth
2 current-plexus)))))
565 ;; Remove forward link created by nema.
566 (let ((new-fwd (delete `(,uid . ,old-snk)
567 (gethash old-src (nth
3 current-plexus)))))
569 (puthash old-src new-fwd (nth
3 current-plexus))
570 (remhash old-src (nth
3 current-plexus))))
571 ;; Remove backward link created by nema.
572 (let ((new-bkw (delete `(,uid . ,old-src)
573 (gethash old-snk (nth
4 current-plexus)))))
575 (puthash old-snk new-bkw (nth
4 current-plexus))
576 (remhash old-snk (nth
4 current-plexus))))
577 ;; Remove record from nema table.
578 (remhash uid (nth
2 current-plexus))))
581 \begin{notate
}{Functions for gathering links
}
582 Links are stored on triples alongside other
587 (defun get-forward-links (uid)
588 "Return all links having given object as source."
589 (mapcar 'car (gethash uid (nth
3 current-plexus))))
591 (defun get-backward-links (uid)
592 "Return all links having given object as sink."
593 (mapcar 'car (gethash uid (nth
4 current-plexus))))
596 \begin{notate
}{On `label-nema'
}
597 Nemas can be given a unique human-readable label in addition
598 to their numeric uid.
602 (defun label-nema (uid label)
603 "Assign the label to the given object."
604 (puthash uid label (nth
5 current-plexus))
605 (puthash label uid (nth
6 current-plexus)))
608 \begin{notate
}{Label to uid and uid to label lookup
}
609 These functions allow the exchange of uid and label.
613 (defun label2uid (label)
614 "Return the unique identifier corresponding to a label."
615 (gethash label (nth
6 current-plexus) nil))
617 (defun uid2label (uid)
618 "Return the label associated to a unique identifier."
619 (gethash uid (nth
5 current-plexus) nil))
622 \subsection{Bulk Operations
}
624 \begin{notate
}{On `download-en-masse'
}
625 Unpack triplets, obtain labels if they exist.
626 Write data in the network to a list, and return.
630 (defun download-en-masse ()
631 "Produce a representation of the database as quintuples."
633 (maphash (lambda (uid tplt)
635 (let ((src (car tplt))
637 (txt (nthcdr
2 tplt)))
638 ; Obtain next label if exists.
639 (setq lbl (gethash uid
640 (nth
5 current-plexus)
642 ; Write data to list.
643 (setq plex (cons `(,uid ,lbl ,src ,snk . ,txt)
645 (nth
2 current-plexus))
646 ; Return list of data.
650 \begin{notate
}{On `upload-en-masse'
}
651 Unpack quintuplets. Plug into tables.
652 Bump up article counter as needed.
657 (defun upload-en-masse (plex)
658 "Load a representation of a database as quintuples into memory."
659 (dolist (qplt plex t)
661 (let ((uid (car qplt))
665 (txt (nthcdr
4 qplt)))
669 (nth
2 current-plexus))
672 (gethash src (nth
3 current-plexus) nil))
673 (nth
3 current-plexus))
677 (gethash snk (nth
4 current-plexus) nil))
678 (nth
4 current-plexus))
681 (puthash uid lbl (nth
5 current-plexus))
682 (puthash lbl uid (nth
6 current-plexus))))
683 ; Bump up nema counter if needed.
684 (when (> uid (cadr current-plexus))
685 (setcar (cdr current-plexus) uid)))))
688 \begin{notate
}{On `add-en-masse'
}
689 Given several articles, add all of them at once.
693 (defun add-en-masse (plex)
694 "Add multiple nemata given as list of quartuplets."
695 (mapcar (lambda (qplt)
696 (let ((uid (next-unique-id)))
697 (add-nema (nth
1 plex)
700 (label-nema uid (car qplt))))
706 \begin{notate
}{Overview of search and query functionality
}
707 We first describe several elementary functions for
708 accessing elements of the network. We then describe a
709 robust search pipeline and show how it is implemented.
712 \begin{notate
}{Various lookup functions
}
713 These functions allow testing and lookup of various elements
719 "Is this a valid uid?"
721 (not (eq z (gethash uid (nth
2 current-plexus) z)))))
724 "List of all valid uid's."
725 (maphash (lambda (key val) key)
726 (nth
2 current-plexus)))
728 (defun ground-p (uid)
729 "Is this nema the ground?"
732 (defun source-p (x y)
733 "Is the former nema the sink of the latter?"
734 (equal x (get-source y)))
737 "Is the former nema the sink of the latter?"
738 (equal x (get-sink y)))
740 (defun links-from (x y)
741 "Return all links from nema x to nema y."
742 (filter '(lambda (z) (source-p x z))
743 (get-backward-links y)))
746 "Does nema x link to nema y?"
747 (when (member x (mapcar
749 (get-backward-links y)))
752 (defun triple-p (x y z)
753 "Do the three items form a triplet?"
758 "Is this object a plexus?"
761 (equal (car x) "*plexus*")))
765 (setq ans (and ans (hash-table-p
770 \subsection{Iteration
}
772 \begin{notate
}{Iterating over a plexus
}
773 These functions allow users to run loops over a plexus without
774 having to delve into its internal structure.
778 (defmacro do-plexus (var res body)
779 `((maphash (lambda (,var val) ,body)
780 (nth
2 current-plexus))
783 (defun map-plexus (func)
787 (push (apply func key) ans))
788 (nth
2 current-plexus))
791 (defun filter-plexus (pred)
795 (when (apply pred key)
797 (nth
2 current-plexus))
801 \begin{notate
}{Additional elementary functions for node access
}
802 These functions give access to the various parts of a node.
\todo{Note:
803 `article-list' hasn't been defined at this point, what is it? Is it
804 something we can straightforwardly derive from `current-plexus'
}
809 (car (nth
0 (cdr (assoc n (cdr article-list))))))
812 (cdr (nth
0 (cdr (assoc n (cdr article-list))))))
815 (nth
1 (cdr (assoc n (cdr article-list)))))
818 (car (nth
2 (cdr (assoc n (cdr article-list))))))
821 (cdr (nth
2 (cdr (assoc n (cdr article-list))))))
824 (mapcar (quote car) (cdr article-list)))
826 (defun get-gnd nil
0)
829 \begin{notate
}{On `search-cond'
} \label{search-cond
}
830 Surround the search within dolist loops on free variables.
831 Wrap no further when finished.
\todo{Upgrade this to concatenate the results together.
832 Also maybe allow options to add headers or to
833 only loop over unique tuplets.
}\todo{Explain; how does this differ from
834 the function defined at Note
\ref{search
}?
}
838 (defmacro search-cond (vars prop)
839 "Find all n-tuplets satisfying a condition"
840 (let ((foo '(lambda (vars cmnd)
843 `(dolist (,(car vars) uids)
844 ,(funcall foo (cdr vars) cmnd))
846 (funcall foo vars prop)))
849 \begin{notate
}{Overview of the search pipeline
}
850 We will implement the search as a pipeline which gradually
851 transforms the query into a series of expressions which produce
852 the sought-after result, then evaluate those expressions.
854 A search query designates predicates apply to the nodes
855 and the network relationships that apply to them. The network relationships
856 function as wildcards.
858 The basic model of the data is triplets that point to other triplets.
859 The following query asks for a
\emph{funny
} link from a
860 \emph{big blue object
} to a
\emph{small green link
} pointing outwards
861 from the big blue object.
863 (((a blue big) (b funny) (c green small)
864 ((b src a) (b snk c) (c src a))
866 The first step of processing is to put the quaerenda in some
867 order so that each item links up with at least one previous item:
877 ((c (green small) ((b snk c) (c src a)))
878 (b (funny) ((b src a)))
881 Note that the order is reversed due to technicalities of
882 implementing `scheduler' --- that is to say, a is first and does
883 not link to any other variable, b is next and links to only a,
884 whilst c is last and links to both a and b.
885 At the same time, we have also rearranged things so that the
886 links to previous items to which a given object are listed
887 alongside that object. The next step is to replace the links with the commands which
888 generate a list of such objects:
890 ((c (green small) ((b snk c) (c src a)))
891 (b (funny) ((b src a)))
895 (intersection (list (get-snk b)) (get-forward-links a)))
897 (intersection (get-backward-links a)))
900 This is done using the function `tplts2cmd', e.g.
902 (tplts2cmd 'c '((b snk c) (c src a)))
904 (intersection (list (get-snk b)) (get-forward-links a))
906 Subsequently, we filter over the predicates:
908 ((c (filter '(lambda (c) (and (green c) (small c)))
909 (intersection (list (get-snk b))
910 (get-forward-links))))
911 (b (filter '(lambda (b) (and (funny b)))
912 (intersection (get-backward-links a)))))
914 This is done with the command `add-filt':
918 '((b snk c) (c src a)))
920 (c (filter (quote (lambda (c) (and (green c) (small c))))
921 (intersection (list (get-snk b))
922 (get-forward-links a))))
924 This routine calls up the previously described routine `tplts2cmd'
925 to take care of the third argument. The last entry,
{\tt (a blue big)
}
926 gets processed a little differently because we don't as yet have
927 anything to filter over; instead, we generate the initial list by
928 looping over the current network:
932 (when (and (blue (get-content node))
933 (big (get-content node)))
934 (setq ans (cons node ans))))
937 This is done by invoking `first2cmd':
939 (first2cmd '(blue big))
943 (when (and (blue (get-content node))
944 (big (get-content node)))
945 (setq ans (cons node ans))))
948 And putting this all together:
951 '((c (green small) ((b snk c) (c src a)))
952 (b (funny) ((b src a)))
955 ((c (filter (quote (lambda (c) (and (green c) (small c))))
956 (intersection (list (get-snk b))
957 (get-forward-links a))))
958 (b (filter (quote (lambda (b) (and (funny b))))
959 (intersection (get-forward-links a))))
962 (when (and (blue (get-content node))
963 (big (get-content node)))
964 (setq ans (cons node ans))))
967 To carry out these instructions in the correct order and generate
968 a set of variable assignments, we employ the `matcher' function.
969 Combining this last layer, we have the complete pipeline:
981 This combination of operations is combined into the `search'
982 function, which can be called as follows:
993 Having described what the functions are supposed to do and how
994 they work together, we now proceed to implement them.
997 \begin{notate
}{On `scheduler'
}
998 The scheduler function takes a list search query and rearranges it
999 into an order suitable for computing the answer to that query.
1000 Specifically, a search query is a pair of lists --- the first list
1001 consists of lists whose heads are names of variables and whose
1002 tails are predicates which the values of the variables should
1003 satisfy and the second list consists of triples indicating the
1004 relations between the values of the variables.
1007 \item new-nodes, a list of items of the form
\verb|(node &rest property)|;
1008 \item \verb|links|, a list of triplets;
1009 \item \verb|sched| is a list whose items consist of triplets of the
1010 form
\newline \verb|(node (&rest property) (&rest link))|.
1013 A recursive function to find linked nodes.
1014 If done, return answer.
1015 New nodes yet to be examined.
1016 Element of remaining-nodes currently under consideration.
1017 List of links between candidate and old-nodes.
1018 List of nodes already scheduled.
1019 Loop through new nodes until find one linked to an old node.
1020 Look at the next possible node.
1021 Find the old nodes linking to the candidate node and record the answer in ``ties''.
1022 Pick out the triplets whose first element is the node under consideration and whose third element is already on the list or vice-versa.
1023 Recursively add the rest of the nodes.
1027 (defun scheduler (new-nodes links sched)
1028 (if (null new-nodes)
1030 (let ((remaining-nodes new-nodes)
1033 (old-nodes (mapcar 'car sched)))
1035 (setq candidate (car remaining-nodes))
1036 (setq remaining-nodes (cdr remaining-nodes))
1038 (filter '(lambda (x)
1040 (and (eq (first x) (car candidate))
1041 (member (third x) old-nodes))
1042 (and (member (first x) old-nodes)
1043 (eq (third x) (car candidate)))))
1045 (scheduler (remove candidate new-nodes)
1047 (cons (list (car candidate)
1053 \begin{notate
}{On `tplts2cmd'
}
1054 \ldots\todo{Explain.
}
1058 (defun tplts2cmd (var tplts)
1062 (cond ((and (eq (third tplt) var)
1063 (eq (second tplt) 'src))
1064 `(get-flk ,(first tplt)))
1065 ((and (eq (third tplt) var)
1066 (eq (second tplt) 'snk))
1067 `(get-blk ,(first tplt)))
1068 ((and (eq (first tplt) var)
1069 (eq (second tplt) 'src))
1070 `(list (get-src ,(third tplt))))
1071 ((and (eq (first tplt) var)
1072 (eq (second tplt) 'snk))
1073 `(list (get-snk ,(third tplt))))
1078 \begin{notate
}{On `add-filt'
}
1079 \ldots\todo{Explain.
}
1083 (defun add-filt (var preds tplts)
1091 (list 'get-txt var)))
1093 ,(tplts2cmd var tplts))))
1096 \begin{notate
}{On `first2cmd'
}
1097 \ldots\todo{Explain.
}
1101 (defun first2cmd (preds)
1103 (dolist (node (get-ids) ans)
1108 (cons pred '((get-txt node))))
1110 (setq ans (cons node ans))))))
1113 \begin{notate
}{On `query2cmd'
}
1114 \ldots\todo{Explain.
}
1118 (defun query2cmd (query)
1119 (let ((backwards (reverse query)))
1122 (list (caar backwards)
1123 (first2cmd (cdar backwards)))
1126 (add-filt (first x) (second x) (third x)))
1127 (cdr backwards))))))
1130 \begin{notate
}{On `matcher'
}
1131 \ldots\todo{Explain.
}
1135 (defun matcher (assgmt reqmts)
1136 (if (null reqmts) (list assgmt)
1140 (matcher (cons (list (caar reqmts) x)
1143 (apply 'intersection
1146 (cdar reqmts)))))))))
1149 \begin{notate
}{How matcher works
}
1150 Here are some examples unrelated to what comes up in searching
1151 triplets which illustrate how matcher works:
1155 (matcher '((x
1)) '((y (list
1 3))
1156 (z (list (+ x y) (- y x)))))
1158 (((z
2) (y
1) (x
1))
1161 ((z
2) (y
3) (x
1)))
1163 (matcher nil '((x (list
1))
1165 (z (list (+ x y) (- y x)))))
1167 (((z
2) (y
1) (x
1))
1170 ((z
2) (y
3) (x
1)))
1173 \begin{notate
}{On `search'
} \label{search
}
1174 \ldots\todo{Explain; how does this differ from
1175 the macro defined at Note
\ref{search-cond
}?
}
1179 (defun search (query)
1186 (list (caar query)))))))
1189 \begin{notate
}{Search convenience functions
}
1190 Several convenience functions for search can be
1191 defined.
\todo{The code here is just a sketch, but hopefully something similar will actually work!
}
1195 (defun triples-given-beginning (node)
1196 "Get triples outbound from the given NODE."
1197 (filter-plexus `(((a ,node))
1201 (defun triples-given-end (node)
1202 "Get triples inbound into NODE."
1203 (filter-plexus `(((b ,node))
1207 (defun triples-given-middle (edge)
1208 "Get the triples that run along EDGE."
1209 (filter-plexus `(((c ,edge))
1213 (defun triples-given-middle-and-end (edge node)
1214 "Get the triples that run along EDGE into NODE."
1215 (filter-plexus `(((c ,edge) (b ,node))
1219 (defun triples-given-beginning-and-middle (node edge)
1220 "Get the triples that run from NODE along EDGE."
1221 (filter-plexus `(((a ,node) (c ,edge))
1225 (defun triples-given-beginning-and-end (node1 node2)
1226 "Get the triples that run from NODE1 to NODE2. Optional
1227 argument VIEW causes the results to be selected
1228 into a view with that name."
1229 (filter-plexus `(((a ,node1) (b ,node2))
1233 (defun triple-exact-match (node1 edge node2)
1234 "Get the triples that run from NODE1 along EDGE to
1236 (filter-plexus `(((a ,node1) (b ,node2) (c ,edge))
1241 \subsection{Scholium programming
}
1243 \begin{notate
}{Scholium programming
}
1244 The next several functions allow us to store and retrieve code
1245 from inside of the network.
1248 \begin{notate
}{On `node-fun'
}
1249 \ldots\todo{Explain.
}
1250 Produce a list of commands to produce temporary bindings.
1251 Produce a list of commands to reset function values.
1255 (defun node-fun (node get-code get-links)
1256 (let ((code (funcall get-code node))
1257 (links (funcall get-links node)))
1266 (mapcar #'(lambda (x)
1273 (mapcar #'(lambda (x)
1274 (if (fboundp (car x))
1276 ',(symbol-function (car x)))
1277 `(fmakunbound ',(car x))))
1281 \begin{notate
}{On `tangle-module'
}
1282 Recursively replace the chunks to recover executable code.
\todo{Explain.
}
1286 (defun tangle-module (node get-cont ins-links)
1288 (funcall get-cont node)
1289 (mapcar #'(lambda (x)
1291 (tangle-module (cdr x)
1294 (funcall ins-links node))))
1297 \begin{notate
}{On `insert-chunk'
}
1298 Given a node and an association list of replacement texts, insert
1299 the chunks at the appropriate places.
1303 (defun insert-chunk (body chunks)
1304 (cond ((null body) nil)
1305 ((null chunks) body)
1306 ((equal (car body) '*insert*)
1307 (cdr (assoc (cadr body) chunks)))
1308 (t (cons (insert-chunk (car body) chunks)
1309 (insert-chunk (cdr body) chunks)))))
1312 \begin{notate
}{Functions for rewriting nemas
}
1313 Several functions for rewriting nemas.
\todo{How does this stuff relate to what's
1314 going on in the vicinity of Note
\ref{update-sink
}?
}
1318 (defun set-src (n x)
1321 (progn (let ((old-backlink
1322 (nth
1 (assoc (get-src n)
1323 (cdr article-list)))))
1324 (setcdr old-backlink
1325 (delete n (cdr old-backlink))))
1327 `(nth
1 (assoc x (cdr article-list)))))
1328 (setcdr new-backlink (cons n (cdr new-backlink))))
1329 (setcar (nth
1 (assoc n (cdr article-list))) x))))
1331 (defun set-txt (n x)
1332 (setcar (cdr (cdr (assoc n (cdr article-list)))) x))
1334 (defun set-snk (n x)
1337 (progn (let ((old-backlink
1338 (nth
3 (assoc (get-snk n)
1339 (cdr article-list)))))
1340 (setcdr old-backlink
1341 (delete n (cdr old-backlink))))
1343 (nth
3 (assoc x (cdr article-list)))))
1344 (setcdr new-backlink (cons n (cdr new-backlink))))
1345 (setcar (nth
3 (assoc n (cdr article-list))) x))))
1347 (defun ins-nod (src txt snk)
1348 (progn (setcdr article-list
1349 (cons (list (car article-list)
1353 (cdr article-list)))
1355 (nth
3 (assoc snk (cdr article-list)))))
1356 (setcdr backlink (cons (car article-list)
1359 (nth
1 (assoc src (cdr article-list)))))
1360 (setcdr backlink (cons (car article-list)
1362 (- (setcar article-list (+
1 (car article-list)))
1)))
1369 (progn (let ((old-backlink
1370 (nth
3 (assoc (get-snk n)
1371 (cdr article-list)))))
1372 (setcdr old-backlink
1373 (delete n (cdr old-backlink))))
1375 (nth
1 (assoc (get-src n)
1376 (cdr article-list)))))
1377 (setcdr old-backlink
1378 (delete n (cdr old-backlink))))
1379 (setcdr article-list
1380 (delete (assoc n (cdr article-list))
1381 (cdr article-list)))
1385 \subsection{Initialization
}
1387 \begin{notate
}{Initialize with a new network
}
1388 For now, we just create one network to import things into. Additional
1389 networks can be added later (see Section
\ref{applications
}).
1393 (set-current-plexus (add-plexus))
1396 \section{An example middle\"end
} \label{middle-end
}
1398 \begin{notate
}{A middle\"end for managing collections of articles
}
1399 This middle\"end is a set of functions that add triples into the
1400 backend. At this stage we basically ignore details of storage, and
1401 rely on the convenience functions defined above as the backend's API.
1402 In principle it would be possible to swap out the backend for another
1403 storage mechanism. We will give an example later on that uses more of
1404 the LISP-specific aspects of the backend implementation.
\todo{Let's
1405 try to be a bit more concrete about this, especially in Section
1406 \ref{farm-demo
}.
} In this example, rather than talking about nemas
1407 and networks, we will talk about
\emph{articles
} and
\emph{scholia
}.
1408 These objects are things that user will want to access, create, and
1409 manipulate. However, we will deal with functions for user interaction
1410 (input, display, and editing) in Section
\ref{frontend
}, not here.
1411 Like the backend, the middle\"end could also be swapped out in
1412 applications where a different kind of data is modelled. And in fact,
1413 we come to some examples of other mid-level interfaces in Section
1417 \subsection{Database interaction
} \label{interaction
}
1419 \begin{notate
}{The `article' function
} \label{the-article-function
}
1420 You can use this function to create an article with a
1421 given name and contents. You can optionally put it in a
1422 list by specifying the heading that it is under.
1426 (defun article (name contents &optional heading)
1427 (let ((coordinates (add-nema name
1430 (when heading (add-nema coordinates "in" heading))
1434 \begin{notate
}{The `scholium' function
} \label{the-scholium-function
}
1435 You can use this function to link annotations to objects.
1436 As with the `article' function, you can optionally
1437 categorize the connection on a given list (cf. Note
1438 \ref{the-article-function
}).
1442 (defun scholium (beginning link end &optional heading)
1443 (let ((coordinates (add-nema beginning
1446 (when heading (add-nema coordinates "in" heading))
1450 \begin{notate
}{Uses of coordinates
}
1451 It is convenient to do further immediate processing of the object
1452 we've created while we still have ahold of the coordinates
1453 returned by `add-nema' (e.g., for importing code
1454 that is adjacent to the article, see Note
1455 \ref{import-code-continuations
}).
1458 \begin{notate
}{On `get-article'
} \label{get-article
}
1459 Get the contents of the article named `name'.
1460 We assume that there is only one such article for now.
1464 ;; Something like this.
1465 (defun get-article (name)
1466 (third (triples-given-beginning-and-middle
1467 name "has content")))
1470 \begin{notate
}{On `get-names'
} \label{get-names
}
1471 This function simply gets the names of articles that have
1472 names -- in other words, every triple built around the
1473 ``has content'' relation.
1477 ;; Something like this.
1478 (defun get-names (&optional heading)
1479 (map first (triples-given-middle "has content")))
1482 \section{An example frontend
} \label{frontend
}
1484 \begin{notate
}{Overview of the frontend
}
1485 The frontend provides a demonstration of Arxana's functionality that
1486 is directly accessible to the user. Specifically, it is used to
1487 import
\LaTeX\ documents into a network structure. They can then be
1488 edited, remixed, saved, browsed, and exported.
\todo{Some of this
1489 functionality still needs to be merged in or written!
}
1492 \subsection{Importing
\LaTeX\ documents
} \label{importing
}
1494 \begin{notate
}{Importing sketch
} \label{importing-sketch
}
1495 The code in this section imports a
document, represented as a
1496 collection of (sub-)sections and notes. It gathers the sections,
1497 sub-sections, and notes recursively and records their content in a
1498 tree whose nodes are places and whose links express the
1499 ``component-of'' relation described in Note
\ref{order-of-order
}.
1501 This representation lets us see the geometric, hierarchical, structure
1502 of the
document we've imported. It exemplifies a general principle,
1503 that geometric data should be represented by relationships between
1504 places, not direct relationships between strings. This is because
1505 ``the same'' string often appears in ``different'' places in any given
1506 document (e.g. a paper's many sub-sections titled ``Introduction''
1507 will not all have the same content).
\todo{Do we need to relax this?
}
1509 What goes into the places is in some sense arbitrary. The key is that
1510 whatever is in or attached to these places must tell us
1511 everything we need to know about the part of the
document associated
1512 with that place (e.g., in the case of a note, its title and contents).
1513 That's over and above the structural links which say how the
1514 places relate to one another. Finally, all of these places and
1515 structural links will be added to a heading that represents the
1516 document as a whole.
1518 A natural convention we'll use is to put the name of any
document
1519 component that's associated with a given place into that place, and
1520 add all other information as annotations.
\todo{Does this contradict
1521 what is said above about Introductions?
}
1523 Following our usual coding convention, functions are introduced
1524 below ``from the bottom up.''
1527 \begin{notate
}{On `import-code-continuations'
} \label{import-code-continuations
}
1528 This function runs within the scope of `import-notes'.
1529 In fact, it is meant to run right after a Note itself
1530 has been scanned. The job of this function is to turn the
1531 series of Lisp chunks or other code snippets that follow a given note
1532 into a scholium attached to that note. Each separate snippet becomes
1533 its own annotation. The ``conditional regexps'' used here only work
1534 with Emacs version
23 or higher.
\todo{I'm noticing a problem with the
1535 way the `looking-at' form behaves. It matches the expression in
1536 question, but then the match-end is reported as one character less
1537 than it supposed to be. Maybe `looking-at' is just not as good as
1538 `re-search-forward'? But it's what seems easiest to use.
}
1542 ;; coords don't exist anymore, now we use uids
1543 (defun import-code-continuations (coords)
1544 (let ((possible-environments
1545 "\\(
1?:lisp\\|idea\\|common\\)"))
1547 (concat "
\n*?\\\
\begin{"
1548 possible-environments
1550 (let* ((beg (match-end
0))
1551 (environment (match-string
1))
1552 (end (progn (search-forward-regexp
1556 (match-beginning
0)))
1557 (content (buffer-substring-no-properties
1560 (scholium (scholium coords
1567 \begin{notate
}{On `import-notes'
} \label{import-notes
}
1568 We're going to make the daring assumption that the ``textual''
1569 portions of incoming
\LaTeX\ documents are contained in ``Notes''.
1570 That assumption is true, at least, for the current
document. The
1571 function takes a buffer position `end' that denotes the end of the
1572 current section. The function returns the count of the number of
1573 notes imported, so that `import-within' knows where to start counting
1574 this section's non-note children.
\todo{Would this same function work
1575 to import all notes from a buffer without examining its sectioning
1576 structure? Not quite, but close! (Could be a fun exercise to fix
1581 (defun import-notes (end)
1583 (while (re-search-forward (concat "\\\
\begin{notate
}"
1585 "\\( +\\\
\label{\\)?"
1589 (match-string-no-properties
1))
1590 (tag (match-string-no-properties
3))
1592 (progn (next-line
1)
1593 (line-beginning-position)))
1595 (progn (search-forward-regexp
1597 (match-beginning
0)))
1598 ;; this has to change
1599 (coords (add-nema name "in" buffername)))
1600 (setq index (
1+ index))
1601 (scholium current-parent
1607 (buffer-substring-no-properties
1609 (import-code-continuations coords)))
1613 \begin{notate
}{On `import-within'
}
1614 Recurse through levels of sectioning in a
document to import
1615 \LaTeX\ code. Children that are notes are attached to the
1616 hierarchical structure by the subroutine `import-notes', called by
1617 this function. Sections are attached directly by `import-within'. We
1618 observe that a note ``has content'' whereas a section does not.
1620 Incidentally, when looking for the end of an importing level, `nil' is
1621 an OK result: that describes the case when we have reached the
1622 \emph{last
} section at this level
\emph{and
} there is no subsequent
1623 section at a higher level.
1627 (defun import-within (levels)
1628 (let ((this-level (car levels))
1629 (next-level (car (cdr levels))) answer)
1630 (while (re-search-forward
1632 "^\\\\" this-level "
{\\(
[^
}\n]*\\)
}"
1633 "\\( +\\\
\label{\\)?"
1636 (let* ((name (match-string-no-properties
1))
1637 (at (add-nema name "in" buffername))
1640 (search-forward-regexp
1641 (concat "^\\\\" this-level "
{.*")
1648 (search-forward-regexp
1654 (index (let ((current-parent at))
1655 (import-notes notes-end)))
1656 (subsections (let ((current-parent at))
1657 (import-within (cdr levels)))))
1659 (let ((coords (car subsections)))
1660 (setq index (
1+ index))
1665 (setq subsections (cdr subsections))))
1666 (setq answer (cons at answer))))
1670 \begin{notate
}{On `import-buffer'
}
1671 This function import a
\LaTeX\
document, taking care of
1672 the high-level, non-recursive, aspects of this operation.
1673 It imports frontmatter (everything up to the first
1674 \verb+
\begin{section
}+), but assumes ``backmatter'' is
1675 trivial, and does not attempt to import it. The imported
1676 material is classified as a ``
document'' with the same
1677 name as the imported buffer.
1679 Links to sections will be made under the ``heading'' of this
1680 document.
\todo{The sectioning levels should maybe be scholia attached
1681 to root-coords, but for some reason that wasn't working so well --
1682 investigate later -- maybe it just wasn't good to run after running
1687 (defun import-buffer (&optional buffername)
1689 (set-buffer (get-buffer (or buffername
1691 (goto-char (point-min))
1692 (search-forward-regexp "\\\
\begin{document}")
1693 (search-forward-regexp "\\\
\section")
1694 (goto-char (match-beginning
0))
1695 (scholium buffername "is a" "
document")
1696 (scholium buffername
1698 (buffer-substring-no-properties
1702 (let* ((root-coords (add-nema buffername "in" buffername))
1704 '("section" "subsection" "subsubsection"))
1705 (current-parent buffername)
1707 (sections (import-within levels))
1710 (let ((coords (car sections)))
1711 (setq index (
1+ index))
1712 (scholium root-coords
1716 (setq sections (cdr sections))))))
1719 \begin{notate
}{On `autoimport-arxana'
} \label{autoimport-arxana
}
1720 This just calls `import-buffer', and imports this
document
1725 (defun autoimport-arxana ()
1727 (import-buffer "arxana-merge.tex"))
1730 \subsection{Browsing database contents
} \label{browsing
}
1732 \begin{notate
}{Browsing sketch
} \label{browsing-sketch
}
1733 This section facilitates browsing of documents represented
1734 with structures like those created in Section
1735 \ref{importing
}, and sets the ground for browsing other
1736 sorts of contents (e.g. collections of tasks, as in
1737 Section
\ref{managing-tasks
}).
1739 In order to facilitate general browsing, it is not enough
1740 to simply use `get-article' (Note
\ref{get-article
}) and
1741 `get-names' (Note
\ref{get-names
}), although these
1742 functions provide our defaults. We must provide the means
1743 to find and display different things differently -- for
1744 example, a section's table of contents will typically
1745 be displayed differently from its actual contents.
1747 Indeed, the ability to display and select elements of
1748 document sections (Note
\ref{display-section
}) is
1749 basically the core browsing deliverable. In the process
1750 we develop a re-usable article selector (Note
1751 \ref{selector
}; cf. Note
\ref{browsing-tasks
}). This in
1752 turn relies on a flexible function for displaying
1753 different kinds of articles (Note
\ref{display-article
}).
1756 \begin{notate
}{On `display-article'
} \label{display-article
}
1757 This function takes in the name of the article to display.
1758 Furthermore, it takes optional arguments `retriever' and
1759 `formatter', which tell it how to look up and/or format
1760 the information for display, respectively.
1762 Thus, either we make some statement up front (choosing our
1763 `formatter' based on what we already know about the
1764 article), or we decide what to display after making some
1765 investigation of information attached to the article, some
1766 of which may be retrieved and displayed (this requires
1767 that we specify a suitable `retriever' and a complementary
1770 For example, the major mode in which to display the
1771 article's contents could be stored as a scholium attached
1772 to the article; or we might maintain some information
1773 about ``areas'' of the database that would tell us up
1774 front what which mode is associated with the current area.
1775 (The default is to simply insert the data with no markup
1778 Observe that this works when no heading argument is given,
1779 because in that case `get-article' looks for
\emph{all
}
1780 place pseudonyms. (But of course that won't work well
1781 when we have multiple theories containing things with the
1782 same names, so we should get used to using the heading
1785 (The business about requiring the data to be a sequence
1786 before engaging in further formatting is, of course, just
1787 a matter of expediency for making things work with the
1792 (defun display-article
1793 (name &optional heading retriever formatter)
1794 (interactive "Mname: ")
1795 (let* ((data (if retriever
1796 (funcall retriever name heading)
1797 (get-article name heading))))
1798 (when (and data (sequencep data))
1801 (funcall formatter data heading)
1802 (pop-to-buffer (get-buffer-create
1803 "*Arxana Display*"))
1804 (delete-region (point-min) (point-max))
1805 (insert "NAME: " name "
\n\n")
1807 (goto-char (point-min)))))))
1810 \begin{notate
}{An interactive article selector
} \label{selector
}
1811 The function `get-names' (Note
\ref{get-names
}) and
1812 similar functions can give us a collection of articles.
1813 The next few functions provide an interactive
1814 functionality for moving through this collection to find
1815 the article we want to look at.
1817 We define a ``display style'' that the article selector
1818 uses to determine how to display various articles. These
1819 display styles are specified by text properties attached
1820 to each option the selector provides. Similarly, when
1821 we're working within a given heading, the relevant heading
1822 is also specified as a text property.
1824 At selection time, these text properties are checked to
1825 determine which information to pass along to
1830 (defvar display-style '((nil . (nil nil))))
1832 (defun thing-name-at-point ()
1833 (buffer-substring-no-properties
1834 (line-beginning-position)
1835 (line-end-position)))
1837 (defun get-display-type ()
1838 (get-text-property (line-beginning-position)
1839 'arxana-display-type))
1841 (defun get-relevant-heading ()
1842 (get-text-property (line-beginning-position)
1843 'arxana-relevant-heading))
1845 (defun arxana-list-select ()
1847 (apply 'display-article
1848 (thing-name-at-point)
1849 (get-relevant-heading)
1850 (cdr (assoc (get-display-type)
1853 (define-derived-mode arxana-list-mode fundamental-mode
1854 "arxana-list" "Arxana List Mode.
1856 \\
{arxana-list-mode-map
}")
1858 (define-key arxana-list-mode-map (kbd "RET")
1859 'arxana-list-select)
1862 \begin{notate
}{On `pick-a-name'
} \label{pick-a-name
}
1863 Here `generate' is the name of a function to call to
1864 generate a list of items to display, and `format' is a
1865 function to put these items (including any mark-up) into
1866 the buffer from which individiual items can then be
1869 One simple way to get a list of names to display would be
1870 to reuse a list that we had already produced (this would
1871 save querying the database each time). We could, in fact,
1872 store a history list of lists of names that had been
1873 displayed previously (cf. Note
\ref{local-storage
}).
1875 We'll eventually want versions of `generate' that provide
1876 various useful views into the data, e.g., listing all of
1877 the elements of a given section (Note
1878 \ref{display-section
}).
1880 Finding all the elements that match a given search term,
1881 whether that's just normal text search or some kind of
1882 structured search would be worthwhile too. Upgrading the
1883 display to e.g.
color-code listed elements according to
1884 their type would be another nice feature to add.
1888 (defun pick-a-name (&optional generate format heading)
1890 (let ((items (if generate
1892 (get-names heading))))
1894 (set-buffer (get-buffer-create "*Arxana Articles*"))
1895 (toggle-read-only -
1)
1896 (delete-region (point-min)
1899 (funcall format items)
1900 (mapc (lambda (item) (insert item "
\n")) items))
1901 (toggle-read-only t)
1903 (goto-char (point-min))
1904 (pop-to-buffer (get-buffer "*Arxana Articles*")))))
1907 \begin{notate
}{On `get-section-contents'
} \label{get-section-contents
}
1908 This function is used by `display-section'
1909 (Note
\ref{display-section
}) to `pick-a-name' as a generator
1910 for the table of contents of the section with the given
1911 name under the given heading.
1913 This function first finds the triples that begin with the
1914 (placed) name of the section, then checks to see which of
1915 these are in the heading of the
document we're examinining
1916 (in other words, which of these links represent structural
1917 information about that
document). It also looks at the
1918 items found at the end of these links to see if they are
1919 sections or notes (``noteness'' is determined by them
1920 having content). The links are then sorted by their
1921 middles (which show the order in which these components
1922 have in the section we're examining). After this ordering
1923 information has been used for sorting, it is deleted, and
1924 we're left with just a list of names in the appropriate
1925 order, together with an indication of their noteness.
1929 (defun get-section-contents (name heading)
1931 (dolist (triple (triples-given-beginning
1932 `(
1 ,(resolve-ambiguity
1933 (get-places name)))))
1934 (when (triple-exact-match
1935 `(
2 ,(car triple)) "in" heading)
1936 (let* ((number (print-middle triple))
1937 (site (isolate-end triple))
1939 (when (triples-given-beginning-and-middle
1944 (print-system-object
1945 (place-contents site))
1950 (lambda (component1 component2)
1951 (< (parse-integer (car component1))
1952 (parse-integer (car component2))))))))
1955 \begin{notate
}{On `format-section-contents'
} \label{format-section-contents
}
1956 A formatter for
document contents, used by
1957 `display-
document' (Note
\ref{display-
document}) as input
1958 for `pick-a-name' (Note
\ref{pick-a-name
}).
1960 Instead of just printing the items one by one,
1961 like the default formatter in `pick-a-name' does,
1962 this version adds appropriate text properties, which
1963 we determine based the second component of
1964 of `items' to format.
1968 (defun format-section-contents (items heading)
1969 ;; just replicating the default and building on that.
1970 (mapc (lambda (item)
1972 (let* ((beg (line-beginning-position))
1974 (unless (second item)
1975 (put-text-property beg end
1976 'arxana-display-type
1978 (put-text-property beg end
1979 'arxana-relevant-heading
1985 \begin{notate
}{On `display-section'
} \label{display-section
}
1986 When browsing a
document, if you select a section, you
1987 should display a list of that section's constituent
1988 elements, be they notes or subsections. The question
1989 comes up: when you go to display something, how do you
1990 know whether you're looking at the name of a section, or
1991 the name of an article?
1993 When you get the section's contents out of the database
1994 (Note
\ref{get-section-contents
})
1998 (defun display-section (name heading)
1999 (interactive (list (read-string
2002 (buffer-name) "): ")
2003 nil nil (buffer-name))))
2004 ;; should this pop to the Articles window?
2005 (pick-a-name `(lambda ()
2006 (get-section-contents
2009 (format-section-contents
2012 (add-to-list 'display-style
2013 '(section . (display-section
2017 \begin{notate
}{On `display-
document'
} \label{display-
document}
2018 When browsing a
document, you should first display its
2019 top-level table of contents. (Most typically, a list of
2020 all of that
document's major sections.) In order to do
2021 this, we must find the triples that are begin at the node
2022 representing this
document \emph{and
} that are in the
2023 heading of this
document. This boils down to treating the
2024 document's root as if it was a section and using the
2025 function `display-section' (Note
\ref{display-section
}).
2029 (defun display-
document (name)
2030 (interactive (list (read-string
2033 (buffer-name) "): ")
2034 nil nil (buffer-name))))
2035 (display-section name name))
2038 \begin{notate
}{Work with `heading' argument
}
2039 We should make sure that if we know the heading we're
2040 working with (e.g. the name of the
document we're
2041 browsing) that this information gets communicated in the
2042 background of the user interaction with the article
2046 \begin{notate
}{Selecting from a hierarchical display
} \label{hierarchical-display
}
2047 A fancier ``article selector'' would be able to display
2048 several sections with nice indenting to show their
2052 \begin{notate
}{Browser history tricks
} \label{history-tricks
}
2053 I want to put together (or put back together) something
2054 similar to the multihistoried browser that I had going in
2055 the previous version of Arxana and my Emacs/Lynx-based web
2056 browser, Nero
\footnote{{\tt http://metameso.org/~joe/nero.el
}}.
2057 The basic features are:
2058 (
1) forward, back, and up inside the structure of a given
2059 document; (
2) switch between tabs. More advanced features
2060 might include: (
3) forward and back globally across all
2061 tabs; (
4) explicit understanding of paths that loop.
2063 These sorts of features are independent of the exact
2064 details of what's printed to the screen each time
2065 something is displayed. So, for instance, you could flip
2066 between section manifests a la Note
\ref{display-section
},
2067 or between hierarchical displays a la Note
2068 \ref{hierarchical-display
}, or some combination; the key
2069 thing is just to keep track in some sensible way of
2070 whatever's been displayed!
2073 \begin{notate
}{Local storage for browsing purposes
} \label{local-storage
}
2074 Right now, in order to browse the contents of the
2075 database, you need to query the database every time. It
2076 might be handy to offer the option to cache names of
2077 things locally, and only sync with the database from time
2078 to time. Indeed, the same principle could apply in
2079 various places; however, it may also be somewhat
2080 complicated to set up. Using two systems for storage, one
2081 local and one permanent, is certainly more heavy-duty than
2082 just using one permanent storage system and the local
2083 temporary display. However, one thing in favor of local
2084 storage systems is that that's what I used in the the
2085 previous prototype of Arxana -- so some code already
2086 exists for local storage! (Caching the list of
2087 \emph{names
} we just made a selection from would be one
2088 simple expedient, see Note
\ref{pick-a-name
}.)
2091 \begin{notate
}{Hang onto absolute references
}
2092 Since `get-article' (Note
\ref{get-article
}) translates
2093 strings into their ``place pseudonyms'', we may want to
2094 hang onto those pseudonyms, because they are, in fact, the
2095 absolute references to the objects we end up working with.
2096 In particular, they should probably go into the
2097 text-property background of the article selector, so it
2098 will know right away what to select!
2101 \subsection{Exporting
\LaTeX\ documents$^*$
}
2103 \begin{notate
}{Roundtripping
}
2104 The easiest test is: can we import a
document into the
2105 system and then export it again, and find it unchanged?
2108 \begin{notate
}{Data format
}
2109 We should be able to
\emph{stably
} import and export a
2110 document, as well as export any modifications to the
2111 document that were generated within Arxana. This means
2112 that the exporting functions will have to read the data
2113 format that the importing functions use,
\emph{and
} that
2114 any functions that edit
document contents (or structure)
2115 will also have to use the same format. Furthermore,
2116 \emph{browsing
} functions will have to be somewhat aware
2117 of this format. So, this is a good time to ask -- did we
2121 \subsection{Editing database contents$^*$
} \label{editing
}
2123 \begin{notate
}{Roundtripping, with changes
}
2124 Here, we should import a
document into the system and then
2125 make some simple changes, and after exporting, check with
2126 diff to make sure the changes are correct.
2129 \begin{notate
}{Re-importing
}
2130 One nice feature would be a function to ``re-import'' a
2131 document that has changed outside of the system, and make
2132 changes in the system's version whereever changes appeared
2133 in the source version.
2136 \begin{notate
}{Editing
document structure
}
2137 The way we have things set up currently, it is one thing
2138 to make a change to a
document's textual components, and
2139 another to change its structure. Both types of changes
2140 must, of course, be supported.
2143 \section{Applications
} \label{applications
}
2145 \subsection{Managing tasks
} \label{managing-tasks
}
2147 \begin{notate
}{What are tasks?
}
2148 Each task tends to have a
\emph{name
}, a
2149 \emph{description
}, a collection of
\emph{prerequisite
2150 tasks
}, a description of other
\emph{material
2151 dependencies
}, a
\emph{status
}, some
\emph{justification
2152 of that status
}, a
\emph{creation date
}, and an
2153 \emph{estimated time of completion
}. There might actually
2154 be several ``estimated times of completion'', since the
2155 estimate would tend to improve over time. To really
2156 understand a task, one should keep track of revisions like
2160 \begin{notate
}{On `store-task-data'
} \label{store-task-data
}
2161 Here, we're just filling in a frame. Since ``filling in a
2162 frame'' seems like the sort of operation that might happen
2163 over and over again in different contexts, to save space,
2164 it would probably be nice to have a macro (or similar)
2165 that would do a more general version of what this function
2170 (defun store-task-data
2171 (name description prereqs materials status
2172 justification submitted eta)
2173 (add-nema name "is a" "task")
2174 (add-nema name "description" description)
2175 (add-nema name "prereqs" prereqs)
2176 (add-nema name "materials" materials)
2177 (add-nema name "status" status)
2178 (add-nema name "status justification" justification)
2179 (add-nema name "date submitted" submitted)
2180 (add-nema name "estimated time of completion" eta))
2183 \begin{notate
}{On `generate-task-data'
} \label{generate-task-data
}
2184 This is a simple function to create a new task matching
2185 the description above.
2189 (defun generate-task-data ()
2191 (let ((name (read-string "Name: "))
2192 (description (read-string "Description: "))
2193 (prereqs (read-string
2194 "Task(s) this task depends on: "))
2195 (materials (read-string "Material dependencies: "))
2196 (status (completing-read
2197 "Status (tabled, in progress, completed):
2198 " '("tabled" "in progress" "completed")))
2199 (justification (read-string "Why this status? "))
2202 (concat "Date submitted (default "
2203 (substring (current-time-string)
0 10)
2205 nil nil (substring (current-time-string)
0 10)))
2207 (read-string "Estimated date of completion:")))
2208 (store-task-data name description prereqs materials
2210 justification submitted eta)))
2213 \begin{notate
}{Possible enhancements to `generate-task-data'
}
2214 In order to make this function very nice, it would be good
2215 to allow ``completing read'' over known tasks when filling
2216 in the prerequisites. Indeed, it might be especially nice
2217 to offer a type of completing read that is similar in some
2218 sense to the tab-completion you get when completing a file
2219 name, i.e., quickly completing certain sub-strings of the
2220 final string (in this case, these substrings would
2221 correspond to task areas we are progressively zooming down
2224 As for the task description, rather than forcing the user
2225 to type the description into the minibuffer, it might be
2226 nice to pop up a separate buffer instead (a la the
2227 Emacs/w3m textarea). If we had a list of all the known
2228 tasks, we could offer completing-read over the names of
2229 existing tasks to generate the list of `prereqs'. It
2230 might be nice to systematize date data, so we could more
2231 easily e.g. sort and display task info ``by date''.
2232 (Perhaps we should be working with predefined database
2233 types for dates and so on.)
2235 Also, before storing the task, it might be nice to offer
2236 the user the chance to review the data they entered.
2239 \begin{notate
}{On `get-filler'
} \label{get-filler
}
2240 Just a wrapper for `triples-given-beginning-and-middle'.
2241 (Maybe we should add `heading' as an optional argument here.)
2245 (defun get-filler (frame slot)
2248 (triples-given-beginning-and-middle frame
2252 \begin{notate
}{On `get-task'
} \label{get-task
}
2253 Uses `get-filler' (Note
\ref{get-filler
}) to assemble the
2254 elements of a task's frame.
2258 (defun get-task (name)
2259 (when (triple-exact-match name "is a" "task")
2260 (list (get-filler name "description")
2261 (get-filler name "prereqs")
2262 (get-filler name "materials")
2263 (get-filler name "status")
2264 (get-filler name "status justification")
2265 (get-filler name "date submitted")
2267 "estimated time of completion"))))
2270 \begin{notate
}{On `review-task'
} \label{review-task
}
2271 This is a function to review a task by name.
2275 (defun review-task (name)
2276 (interactive "MName: ")
2277 (let ((task-data (get-task name)))
2279 (display-task task-data)
2280 (message "No data."))))
2282 (defun display-task (data)
2284 (pop-to-buffer (get-buffer-create
2285 "*Arxana Display*"))
2286 (delete-region (point-min) (point-max))
2287 (insert "NAME: " name "
\n\n")
2288 (insert "DESCRIPTION: " (first data) "
\n\n")
2289 (insert "TASKS THIS TASK DEPENDS ON: "
2290 (second data) "
\n\n")
2291 (insert "MATERIAL DEPENDENCIES: "
2292 (third data) "
\n\n")
2293 (insert "STATUS: " (fourth data) "
\n\n")
2294 (insert "WHY THIS STATUS?: " (fifth data) "
\n\n")
2295 (insert "DATE SUBMITTED:" (sixth data) "
\n\n")
2296 (insert "ESTIMATED TIME OF COMPLETION: "
2297 (seventh data) "
\n\n")
2298 (goto-char (point-min))
2299 (fill-individual-paragraphs (point-min) (point-max))))
2302 \begin{notate
}{Possible enhancements to `review-task'
}
2303 Breaking this down into a function to select the task and
2304 another function to display the task would be nice. Maybe
2305 we should have a generic function for selecting any object
2306 ``by name'', and then special-purpose functions for
2307 displaying objects with different properties.
2309 Using text properties, we could set up a ``field-editing
2310 mode'' that would enable you to select a particular field
2311 and edit it independently of the others. Another more
2312 complex editing mode would
\emph{know
} which fields the
2313 user had edited, and would store all edits back to the
2314 database properly. See Section
\ref{editing
} for more on
2318 \begin{notate
}{Browsing tasks
} \label{browsing-tasks
}
2319 The function `pick-a-name' (Note
\ref{pick-a-name
}) takes
2320 two functions, one that finds the names to choose from,
2321 and the other that says how to present these names. We
2322 can therefore build `pick-a-task' on top of `pick-a-name'.
2329 (triples-given-middle-and-end "is a" "task")
2332 (defun pick-a-task ()
2337 (mapc (lambda (item)
2338 (let ((pos (line-beginning-position)))
2340 (put-text-property pos (
1+ pos)
2341 'arxana-display-type
2343 (insert "
\n"))) items))))
2345 (add-to-list 'display-style
2346 '(task . (get-task display-task)))
2349 \begin{notate
}{Working with theories
}
2350 Presumably, like other related functions, `get-tasks'
2351 should take a heading argument.
2354 \begin{notate
}{Check display style
}
2355 Check if this works, and make style consistent between
2356 this usage and earlier usage.
2359 \begin{notate
}{Example tasks
}
2360 It might be fun to add some tasks associated with
2361 improving Arxana, just to show that it can be done...
2362 maybe along with a small importer to show how importing
2363 something without a whole lot of structure can be easy.
2366 \begin{notate
}{Org mode integration
}
2367 The ``default'' task manager on Emacs is Org mode. It would be good
2368 to provide integration between Org mode and Arxana. This is one of
2369 the first things that Emacs people ask about when they hear about
2373 \subsection{``Modelling mathematics the way it is really done''
} \label{farm-demo
}
2375 \begin{notate
}{A demonstration
}
2376 In a paper for the
5th ACM SIGPLAN International Workshop on
2377 Functional Art, Music, Modelling and Design (FARM
2017), we talk about
2378 how Arxana can be applied to model mathematical proofs. A rather
2379 advanced form of ``mathematical knowledge management'' is proposed,
2380 that integrates dialogue, heuristics, and that ultimately moves in the
2381 direction of an AI system. In this section, we should walk through
2385 \section{Conclusion
} \label{conclusion
}
2387 \begin{notate
}{Ending and beginning again
}
2388 This is the end of this Arxana demo system. Contributions that
2389 support the development of the Arxana project are welcome.
2394 \section{Appendix: A simple literate programming system
} \label{appendix-lit
}
2396 \begin{notate
}{The literate programming system used in this paper
}
2397 This code defines functions that grab all the Lisp portions of this
2398 document, and evaluates the Emacs Lisp sections. It requires that the
2399 \LaTeX\ be written in a certain consistent way. The function assumes
2400 that this
document is the current buffer.
2403 (defvar lit-code-beginning-regexp
2404 "^\\\
\begin{elisp
}")
2406 (defvar lit-code-end-regexp
2409 (defvar lit-count
0)
2413 (lit-process 'eval))
2415 (defun lit-process (&optional code)
2417 (setq lit-count (
1+ lit-count))
2419 (let ((to-buffer (concat "*Lit Code " (int-to-string
2421 (from-buffer (buffer-name (current-buffer))))
2422 (set-buffer (get-buffer-create to-buffer))
2424 (set-buffer (get-buffer-create from-buffer))
2425 (goto-char (point-min))
2426 (while (re-search-forward
2427 lit-code-beginning-regexp nil t)
2428 (let* ((beg (match-end
0))
2429 (end (save-excursion
2430 (search-forward-regexp
2431 lit-code-end-regexp nil t)
2432 (match-beginning
0)))
2433 (match (buffer-substring beg end)))
2435 (set-buffer to-buffer)
2439 (set-buffer to-buffer)
2441 ;; (kill-buffer (current-buffer))
2444 (switch-to-buffer to-buffer))))))
2448 \begin{notate
}{A literate style
}
2449 Ideally, each function will have its own Note to introduce
2450 it, and will not be called before it has been defined. I
2451 sometimes make an exception to this rule, for example,
2452 functions used to form recursions may appear with no
2453 further introduction, and may be called before they are