Yet another microkanren implementation in Python. Still work in progress.
Features:
- Lists and namedtuples unification
- Disequality constraints
- Type constraints
- Reasonably fast non-relational arithmetic
from mk.arithmetic import add, lte, mul
from mk.core import disj, eq
from mk.dsl import conjp, delay, fresh
from mk.run import run
from mk.unify import Var
@delay
def numbers(a, b, out):
return disj(
eq(a, out),
fresh(lambda n: conjp(
add(a, 1, n),
lte(n, b),
numbers(n, b, out)
))
)
a = Var()
b = Var()
goal = conjp(
numbers(0, 256, a),
mul(a, b, 65535)
)
print(list(run(0, (a, b), goal)))
# [(1, 65535), (3, 21845), (5, 13107), (15, 4369),
# (17, 3855), (51, 1285), (85, 771), (255, 257)]
Full source with quines generation: examples/lisp_interpreter.py
from mk.core import eq, eqt
from mk.dsl import conde, conjp, delay, fresh
from mk.ext.lists import no_item
@delay
def eval_expr(exp, env, out):
return conde(
# Quoted value - 'a
(eq([quote, out], exp), no_item(out, is_closure), missing(quote, env)),
# List constructor - (list a b c)
fresh(lambda lst: conjp(
eq([list_, lst, ...], exp),
missing(list_, env), eval_list(lst, env, out),
)),
# Function call - (fn a)
fresh(4, lambda var, body, cenv, arg: conjp(
eval_list(exp, env, [Closure(var, body, cenv), arg]),
eval_expr(body, Env(var, arg, cenv), out),
)),
# Function definition - (lambda (x) body)
fresh(2, lambda var, body: conjp(
eq([lambda_, [var], body], exp), eq(Closure(var, body, env), out),
eqt(var, Symbol), missing(lambda_, env),
)),
# Variable reference - a
(eqt(exp, Symbol), lookup(exp, env, out)),
)
eq(a, b)
- construct goal that succeeds whena
can be unified withb
eqt(a, b)
- construct goal that succeeds when type ofa
can be unified withb
disj(goal_1, goal_2)
- construct composite goal that succeeds whengoal_1
orgoal_2
succeedconj(goal_1, goal_2)
- construct composite goal that succeeds when bothgoal_1
andgoal_2
succeed
neq(a, b)
- negation ofeq(a, b)
neqt(a, b)
- negation ofeqt(a, b)
add(a, b, c)
- construct goal that succeeds whena + b == c
sub(a, b, c)
- construct goal that succeeds whena - b == c
mul(a, b, c)
- construct goal that succeeds whena * b == c
div(a, b, c)
- construct goal that succeeds whena // b == c
gte(a, b)
- construct goal that succeeds whena >= b
lte(a, b)
- construct goal that succeeds whena <= b
disjp(goal, ...)
- n-arydisj
conjp(goal, ...)
- n-aryconj
conde((goal, ...), (goal, ...), ...)
- shortcut fordisjp(conjp(goal, ...), conjp(goal, ...), ...)
no_item(a, predicate)
- construct goal that fails whena
is list that contains item, possibly in nested list, for whichpredicate
is true.
no_item(a, predicate)
- construct goal that fails whena
is tuple that contains item, possibly in nested tuple, for whichpredicate
is true.
@delay(goal_constructor)
- decorator for recursive goal constructorsfresh(goal_constructor)
- constructs goal that callsgoal_constructor
with fresh variablefresh(n, goal_constructor)
- constructs goal that callsgoal_constructor
withn
fresh variables
run(n, query, goal)
- rungoal
, apply resulting substitution toquery
, taken
first results