This guide describes how n3-to-eyemind translates an N3 document and how
eyemind-to-n3 reads the conclusions back.
Triples and terms
Each N3 triple becomes rdf(Subject, Predicate, Object). The document’s own
triples are facts:
:Socrates a :Human.
fact(rdf(iri("http://example.org/socrates#Socrates"), iri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), iri("http://example.org/socrates#Human")))
| N3 | eyemind term |
|---|---|
| IRI | iri("http://example.org/a") |
| Blank node | bnode('n3', Label) |
Integer, xsd:integer |
an integer, exact at any size |
Decimal, double, xsd:decimal, xsd:double |
a float |
String, xsd:string |
literal(Text, datatype("http://www.w3.org/2001/XMLSchema#string")) |
| Other typed literal, boolean | literal(Text, datatype(IRI)) |
| Language string | literal(Text, lang(Tag)), or lang(Tag, Direction) |
| Collection | a list, [A, B, C] |
| Quoted formula | graph([rdf(S, P, O), ...]) |
| RDF 1.2 triple term | triple(S, P, O) |
| Variable in a quoted formula outside rules | qvar(Name) |
IRIs, blank nodes, literals and triple terms are written as rdf-interchange
writes them, so its rdf-to-eyemind data and the rules of either package
mix. eyemind indexes small ground compounds such as iri(...) like atoms, so
a pattern with a constant IRI finds its matches directly.
Rules
| N3 | eyemind |
|---|---|
{ B } => { H }. |
implies(B, H), a forward rule |
{ H } <= { B }. |
implied_by(H, B) per triple of H, a backward rule |
{ B } log:query { H }. |
implies(B, result(H)), and query(result(S, P, O)) |
{ B } => false. |
contradiction(B), which halts the run |
{ A } => { { B } => { C } }. |
implies(A, [implies(B, C), rdf(graph(B), log:implies, graph(C))]) |
A rule’s ?variables and blank nodes are eyemind variables. A blank node
only the conclusion mentions is, in eyemind, the Skolem term of the firing,
skolem(Rule, Variable, Arguments); eyemind-to-n3 writes it as a blank
node, the same one for the same firing. A rule that concludes a rule also
concludes it as a triple, its own variables quoted as qvar(Name) and
qbnode(Label), because N3 reasoners report derived rules.
Without log:query the program states no query, so eyemind’s conclusions are
the triples its forward rules derive that were not already facts.
Built-ins
A body triple whose predicate is a built-in becomes a goal
n3_<namespace>_<name>(Subject, Object), defined by backward rules written in
eyemind and copied into the program with the helpers it needs. Built-in steps
are ordinary derivations, so a proof cites them like any other.
| N3 | eyemind |
|---|---|
?S log:notIncludes { F } |
not(F), the formula’s own variables anonymous |
?S log:includes { F } |
the goals of F |
(T { F } L) log:collectAllIn ?S |
findall(T, F, L) |
({ A } { B }) log:forAllIn ?S |
not([A, not(B)]) |
F1 log:includes F2, formulas |
n3_includes(F1, F2), each triple of F2 in F1 |
B log:implies H, H log:impliedBy B in a premise |
rdf(B, iri(Predicate), H) or its reverse, matching source and derived rule descriptions |
((L) P) list:map R |
findall(Y, [n3_member(X, L), rdf(X, P, Y)], R) |
log:equalTo, log:notEqualTo |
unify, not_unify for a constant that is no formula; formulas as sets, matched by unification |
crypto:md5, sha, sha256, sha512 |
eyemind’s digest of the string’s UTF-8 bytes, in hexadecimal |
string:lowerCase, upperCase, …IgnoringCase |
eyemind’s atom_lower, atom_upper: Unicode case conversion |
L log:skolem I |
https://eyereasoner.github.io/.well-known/genid/ and the SHA-256 of term_text(L) |
(F1 … Fn) log:conjunction F |
the triples of all Fi, sorted, each once |
F log:conclusion C |
n3_conclusion_of(F, C), a fact per formula the document states, its closure computed while compiling |
"…" log:parsedAsN3 F |
the string parsed while compiling, compared with F as a set |
S string:matches P, notMatches |
regex_match(P, S), not(regex_match(P, S)) |
(S P R) string:replace U |
regex_replace(P, S, R, U), every match replaced |
(S P) string:scrape G |
the first group of the first match, once(regex_find(P, S, [_, G, ..._])) |
A built-in whose formula is neither written in the rule nor bound when it
runs, such as log:includes with a literal object, compiles to 'fail'.
log:equalTo and log:notEqualTo compare two formulas as sets of triples,
sorting each, and other terms by unification.
A built-in whose arguments are all variables that no other goal of the premise mentions has nothing to compute; it holds without binding anything, as in N3 reasoners, and its variables stay universal in any rule the conclusion states. A rule is also stated as a triple, its variables quoted, when a rule’s premise has a variable predicate, or a rule or query matches an explicit rule predicate. This includes backward premises and nested formulas. A general triple query alone does not expose source rules. Matching a rule does not solve its premise, and a matching rule can find itself. A derived rule is already reported as a triple, so it can be matched too.
math:greaterThan and the other comparisons also order "INF" and "-INF"
as xsd:float or xsd:double: -INF before every number, INF after.
Documents
eyemind reads nothing while it reasons, so the commands fetch the documents
log:semantics, log:semanticsOrError and log:content read before
compiling: file: IRIs from disk, http: and https: ones over the network.
These are the IRIs written as the subject of one of those built-ins, and
those the document’s facts state at a place a rule’s pattern finds the
subject, as :doc :source <...> for { :doc :source ?d. ?d log:semantics ?f }.
The program states each one as facts: n3_content(iri(U), Text),
n3_semantics(iri(U), Formula) for text that parses as N3, and
n3_document_error(iri(U), Message) otherwise, written as
error(dereference_failed,U) or error(parse_error,Message).
compileN3 and reasonN3 take them as documents, which
fetchSourceDocuments fetches.
Lists written as triples
A list can be written out as rdf:first and rdf:rest triples from a blank
node, ending in rdf:nil. Such a description is a different term from the
collection ( ... ), so a triple pattern does not match one with the other.
A built-in reads both as a list: before it, each ?variable or blank node
argument, and each such item of a written list, passes through
n3_as_list(T, L), which follows the description, nested descriptions too,
and passes anything else on unchanged. Only a document that mentions
rdf:first, rdf:rest or rdf:nil gets these steps, since only it can write a list
out, and they are left out inside a negation or collection, where reading
rdf:first triples would make the rule depend on every rule that concludes
them.
A scope is the subject of log:includes or log:notIncludes, or the object
of log:collectAllIn or log:forAllIn. A variable that appears nowhere else
in the rule, or a constant such as 1, means the whole store.
Patterns are eyemind’s: ECMAScript regular expressions in Unicode mode. A
constant pattern written for a dialect that lets any character be escaped or
a brace stand for itself, as in "a\\:b" or "", is rewritten to
mean the same there: the backslash before other punctuation is dropped, and
a brace that is no quantifier is escaped.
Numeric built-ins compare and return values, so 2 and 2.0 agree as
objects. Integer division that is exact gives an integer, math:rounded
rounds halves upward, and sums and products run from the left, as written.
list:append also compares bound numeric elements by value, while preserving
the input terms when it constructs a result. list:length requires an
integer result, including integer subtypes, and rejects a decimal or float
even when its value is integral. log:dtlit can extract a language string’s
lexical form and rdf:langString datatype without discarding its language.
In reverse, log:uri rejects characters forbidden in an IRI.
Strings of an integral float, below 10^15, are written without a fraction,
29 rather than 29.0.
Replacement strings escape a dollar sign or backslash with a backslash.
string:replace translates these escapes while reasoning, including when
the replacement arrives through a variable or an RDF list. Unescaped
capture references such as $1 retain their meaning.
Evaluation order
eyemind solves a body from left to right. N3 does not fix an order, so the
compiler moves a built-in after the ordinary triples that bind its inputs:
all the variables of a test such as math:lessThan or log:notEqualTo, or
the subject of a function such as math:sum. A built-in that also runs
backward, such as list:firstRest, stays where it is when its object is
bound. In a backward rule the conclusion’s variables count as bound, since
the calling goal supplies them.
Proofs
eyemind-n3 runs the compiled program through eyemind’s library, and
reasonN3(text, { proof: true }) returns eyemind’s proof, which eyemind
checks before returning it. The built-ins choose between modes with
is_var, which a proof records as it was called.
