This is both a document and a program.
A Software Project is composed of many linked pieces of information.
Documentation tools, IDE, Issue Managers fragment this information: you had to reconstruct a knowledge-base inside your mind.
Doknil is a literate-programming tool that can link chunks of information, code and all other aspects of a software development project, so you can navigate between them.
We will use Doknil for describing Doknil and its implementation.
document and design Doknil using examples in this document;
implement a first version of Doknil using the hyper-literate approach;
implement and document a second version of Doknil using the previous version of the tool;
A software project starts with a specification. In Doknil a specification is a documentation chunk, linked to other design and code chunks.
(!chunk <s> (MD "We need to check if there are palindrome words in a text.")) (!assert <s> is-a specification)
Then we can specify the design and the implementation, and other important details about the code.
(!chunk <e> (MD "A palindrome is a phrase which reads the same backward and forward.")) (!assert <e> is-a definition-of <s>) (!chunk <d> (MD "We can assume not very long texts and use plain strings.")) (!assert <d> is-a design-note of <s>) (!chunk <c> (RacketCode (define (palindromb str) (let* ([lst (string->list (string-downcase str))] [slst (remove* '(#\space) lst)]) (string=? (list->string (reverse slst)) (list->string slst)))))) (!assert <c> is-a implementation-of <d>) (chunk <cp> (URL "http://www.rosettacode.org/wiki/Palindrome_detection#Racket")) (!assert <c> is-a derived-work-of <cp>)
In a specification technical terms are importants. So we can further formalize our example, and apply also some syntax sugars.
(!chunk <palindrome> #:short-name "palindrome" (MD "A phrase which reads the same backward and forward.") (!assert <this> is-a definition)) (!chunk <s> (MD "We need to check if there are @<palindrome> words in a text.") (!assert <this> is-a specification)) (!chunk <plain-string> #:short-name "plain-string" (MD "An array of characters.") (!assert <this> is-a definition)) (!chunk <d> (MD "We can assume not very long texts and use @<plain-strings>.") (!assert <this> is-a design-note of <s>)) (chunk <cp> (URL "http://www.rosettacode.org/wiki/Palindrome_detection#Racket")) (!chunk <c> (RacketCode (define (palindromb str) (let* ([lst (string->list (string-downcase str))] [slst (remove* '(#\space) lst)]) (string=? (list->string (reverse slst)) (list->string slst))))) (!assert <this> is-a '(implementation-of <d>) '(derived-work-of <cp>)))
Doknil is both a literate-programming language (LP) and a knowledge-base language (KB). The LP part is used for generating the code, while the KB for understanding and navigating inside a code base. The Doknil name is composed from Dok (i.e. documents) and Knil (i.e. link written in reverse), so it means links (i.e. relationships) between documents.
Doknil supports an hierarchy of relations, e.g. is-a project-of subsumes also the generic relation is part-of and is related-to. This is useful in queries, during code navigation.
Doknil supports an hierarchy of contexts, e.g. facts about /World/Asimov/FoundationSeries can share some base facts with /World, but not vice-versa. This is useful for reusing facts and for representing differences between different contexts and branches, e.g. different implementations or design of the same project.
Doknil supports tags for annotating facts and reusing them in another context in a convenient way.
Doknil is a simple and limited ontology specification language for linking small pieces of information and documents.
Doknil has derivation rules, but they are few and built-in, i.e. the user can not specify new rules. From an expressive point of view, Doknil is more powerful than RDF, because it has derivation rules, but less powerful than OWL. Contrary to OWL, Doknil can manage directly contexts, i.e. facts that are true only inside a certain domain/context.
The plans are these:
the Doknil compiler read a Doknil source-code, represented as Racket DSL, and it will produce a SQLite database with the content of the KB
a special Doknil viewer tool can read the content of this database, and navigate inside the KB
Doknil code can be compiled using the Racket compiler
Different versions of the same application can have different entry modules, importing the common code, and you can choose which entry point to use
A chunk can have one or more types. Types can be hierarchical.
(!define-type '(company) '(document '(scientific-paper) '(book)))
Roles are relationships between two chunks: a subject and an object. Roles can be hierarchical.
(!define-role '(related-to '(part-of #:owner #t '(department-of) '(project-of) '(task-of '(issue-of) '(feature-of)))))
Facts specify the type or the role of a chunk respect another chunk.
(!assert <c> is-a company) (!assert <d> is-a department-of <c>) (!assert <p> is-a project-of <d>) (!assert <i> is-a issue-of <p>) (!assert <k> '(is-a document) '(is related-to <i>))
Queries are used for navigating inside the KB. Doknil is meant to be used from humans, so queries reults will be truncated if they contain too much elements.
(!get '(?D is-a '(document) '(related-to <d>)))
Roles are hierarchical, so one can derive the generic role from a specific role. For example if ?D is an issue-of a project ?P, then ?D is also related-to ?P, because related-to is a less specific role. In this way, queries can be not too much specific.
If a role has the attribute #:owner #t, then every chunk that has this role, is considered a part of the object chunk and all other parents, following some owner hierarchy. For example in <d> is-a department-of <c>, every task of <d> will be also a task of the owner company <c>.
Facts can be valid only inside a certain context. A context can be a certain domain of discourse, or a paper, or an author general idea, or a company view of the world, or a project. Contexts form an hierarchy.
(!in-new-context World (!assert <mars> is-a planet) (!in-new-context LordOfTheRings (!assert <gondor> is-a city)) (!in-new-context Asimov (!in-new-context FoundationSeries (!assert <trantor> is-a planet))))
Facts are propagated automatically from parent to child contexts, but not from children to parent contexts. So every fact true in World is also true in /World/Asimov/FoundationSeries, but not vice-versa.
If there are exceptions to this rule, then a set of facts (i.e. facts with the same tag) can be explicitely excluded from a child context.
Facts can be tagged. These tags can be used for including (i.e. reusing) or excluding (i.e. branching) group of facts with the same tag, between different contexts.
Tags do not introduce namespaces, because they can be added freely to a set of facts when a new context is introduced, i.e. they have only a pragmatic utility. Tags cannot be accessed by queries, but they are used only internally from the inference engine.
In this example there is a book and a related cinematographic adaption. There are differences between the two.
(!assert <b> is-a book) (!in-context <b> (!assert <mary> is-a character) (!assert <smith> is-a character) (!new-tag *no-in-film* (!assert <elisa> '(is-a character) '(is mother-of <smith>)))) (!assert <f> '(is-a film) '(is based-on <b>)) (!in-context <f> (!include-facts <b>) (!exclude-facts <b>/*in-in-film*))
!exclude-facts has more priority than !include-facts, so it must be specified later.
Inclusion and exclusion of a branch, without specifying the tag, affects all the facts (tagged or not tagged) of the branch.
Inclusion and exclusion have a different semantic. If a branch A includes a branch B/C/**tag1** then all facts of B/C/**tag1** are included, but not the facts inside B/C that are not also inside B/C/**tag1**. Also facts inside B are included, because a branch does not make sense, without its parent. On the contrary, if A/B/C exclude A/B/**tag1**, then all other facts outside the tag are not effected. By the way, this is the normal behaviour one expect.
Some contexts can capture advanced concepts like: facts true in a certain time-frame; facts true in a certain transaction; facts true in a certain revision of a document.
These virtual contexts and related facts can be created and managed automatically from the system.
In this example
(!assert <n> is-a novel) (!in-context <n> (!assert <smith> is-a character))
<n> plays also the role of a context, because <smith> exists only inside <n> context.
Doknil assumes usually the closed-world-assumption, i.e. what is not currently known to be true, is false.
Doknil does not support reification of facts, so facts can not be subject of discourse. This simplify the semantic of the language.
contexts and roles can be used as subjects and objects of facts. So
This is useful in particular on the UI/IDE side for documenting a Doknil schema, using Doknil.
Extensional facts are facts explicitely specified, while intensional facts are facts derived by derivation rules. In Doknil there are few and fixed derivation rules, deriving facts according role, context and owner hierarchy.
Other intensional facts can be added automatically for the IDE and related tool, when they analize the content of chunks.
- (require rackunit)