knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "man/figures/README-", out.width = "100%" )
The goal of logician
is to do logic programming inspired by datalog/prolog in R.
It is written in R without any third-party package dependencies. It targets interactive use
and smaller instances of logical programs.
Non-goal: be fully prolog compatible and super fast.
Side-goal: experiment and have fun.
~~You can install the released version of logician from CRAN with:~~
install.packages("logician")
Or the current github version from here:
remotes::install_github("dirkschumacher/logician")
The general idea is to query a database of facts and rules and ask questions.
logician
tries prove that your query is either true
or false
. Since you
can use variables, there might be multiple assignments to variables that make
your query true
. logician
will return these one by one as an iterator.
This is all work in progress and still a bit hacky, but usable.
library(logician)
Here we define a database with two types of elements:
A
and B
exists if A
and B
are connected ORA
is connected to an intermediate node Z
and there exists a path from
Z
to B
.database <- logician_database( connected(berlin, hamburg), connected(hamburg, chicago), connected(chicago, london), connected(aachen, berlin), connected(chicago, portland), connected(portland, munich), path(A, B) := connected(A, B), path(A, B) := connected(A, Z) && path(Z, B) )
iter <- logician_query(database, path(berlin, hamburg)) iter$next_value()
iter <- logician_query(database, path(berlin, munich)) iter$next_value()
At last let's find all nodes berlin
is connected to.
iter <- logician_query(database, path(berlin, X)) iter$next_value() iter$next_value() iter$next_value() iter$next_value() iter$next_value() iter$next_value()
Unlike prolog, we run on a host language and can integrate R into the evaluation of rules.
The only requirement is that the R expression returns a length 1 logical and does not depend
on anything outside the globalenv
. If that is good idea remains to be seen :)
One downside that the list of possible results could be infinite.
Any expression in the r
clause will be treated as an R expression that is
not unified as the usual clauses. All variables need to be bound before
it can be used.
database <- logician_database( number(1), number(2), number(3), number(4), number(5), sum(A, B) := number(A) && number(B) && r(A + B > 3) )
iter <- logician_query(database, sum(1, B)) iter$next_value() iter$next_value() iter$next_value() iter$next_value()
You could also think of the database
as a real database. Where each fact
is a row of a specific table. rules
are additional logical structures of
your data. Then you can use logical programming instead of SQL to query
your data.
At the moment this is not as practical though as it could be.
database <- logician_database( # the employee table aka relation employee(bart), employee(homer), employee(marge), employee(maggie), employee(lisa), # the payment table salary(bart, 100), salary(homer, 100), salary(marge, 120), salary(maggie, 140), salary(lisa, 180), # reporting hierarchy manages(lisa, maggie), manages(lisa, marge), manages(marge, homer), manages(marge, bart), # direct and indirect reports reports_to(A, B) := manages(B, A), reports_to(A, B) := manages(B, X) && reports_to(A, X), # a salary query makes_more(A, X) := salary(A, Y) && r(Y > X + pi) # using pi here to show that certain R symbols from the globalenv # can be used. )
# who reports to lisa? iter <- logician_query(database, reports_to(A, lisa)) iter$next_value() iter$next_value() iter$next_value() iter$next_value() iter$next_value() # and who makes more than 140 + pi? iter <- logician_query(database, makes_more(A, 140)) iter$next_value() iter$next_value()
And instead of this manual database, you generate a database with real world data. Though the query system might slow for large amount of at this point.
The API has two components: one aimed at interactive use and another one when you want to program with it (e.g. embed it into another package).
logician_database
a helper function to construct a database with the help of
non-standard evaluation. A database is just a list of fact
s and rule
s
that you can also construct yourself using helper functions such
as fact
, rule
, atom
, char
, clause
, variable
, int
and r_expr
.
logician_query
main query function for interactive use. You can use head
on an iterator to return the first n
results (if they exists).
logician_query_
same as logician_query
but expects a clause
or r_expr
.
facts
and rules
.fact
is a clause
.clause
has a name and a list of arguments
.atom
, int
, char
or var
.rule
has a head
and a body
.head
is a clause
.body
is a list of clauses
or R expressions
.logician_query
supports only one clause at the moment. If you want to query
for multiple clauses at one, create a rule for that query.covr::package_coverage()
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.