Packages

Guesswork is a logic programming library for Elixir. It is heavily inspired by Prolog, but attempts to use idiomatic Elixir when expressing problems and their solutions.

Current section

Files

Jump to
guesswork lib guesswork ast stop.ex
Raw

lib/guesswork/ast/stop.ex

defmodule Guesswork.Ast.Stop do
@moduledoc """
Defines when the search for answer sets should stop.
Note that negation is not considered here.
## Meeting Stop Conditions
Stop conditions can be met either through substitution or unification.
Substitution is relatively simple; if all of the inputs are concrete when
`Guesswork.Ast.Statement.resolve/2` is called, an empty stream is returned if
the stop condition is met.
However, if the stop condition is resolved met unification, one must be careful
to define the condition (at least partially) in concrete terms.
Otherwise, the stop coniditon will never be met and the query will result in
an infinate stream.
"""
defstruct [:inputs, :func]
alias Guesswork.Ast.Term
alias Guesswork.Ast.Variable
alias Guesswork.Ast.Statement.Opts
alias Guesswork.Answer
alias Guesswork.Answer.Computation
alias Guesswork.Telemetry.EventHandler
@type t() :: %__MODULE__{inputs: [Term.entity() | Variable.t()], func: Computation.func()}
@spec new([Variable.t() | Term.entity()], Computation.func()) :: t()
def new(inputs, func), do: %__MODULE__{inputs: inputs, func: func}
defimpl Guesswork.Ast.Term do
alias Guesswork.Ast.Stop
def concrete?(_), do: false
def get_variables(%Stop{inputs: inputs}), do: Enum.filter(inputs, &match?(%Variable{}, &1))
end
defimpl Guesswork.Ast.Statement do
alias Guesswork.Ast.Stop
def find_statements(statement, fun) do
if fun.(statement) do
[statement]
else
[]
end
end
def simplify(statement) do
statement
end
def substitute(%Stop{inputs: inputs} = statement, env) do
%Stop{statement | inputs: Enum.map(inputs, &sub(&1, env))}
end
defp sub(%Variable{} = var, env) do
case Map.fetch(env, var) do
:error -> var
{:ok, val} -> val
end
end
defp sub(val, _env), do: val
def resolve(%Stop{inputs: inputs, func: func}, %Opts{query_id: query_id}) do
comp = Computation.new(inputs, func)
case Computation.attempt_apply(comp) do
:error ->
[Answer.new(query_id) |> Answer.put_stop_condition(comp)]
{:ok, got} ->
:telemetry.execute(EventHandler.computation_event_name(), %{tests: 1}, %{
query_id: query_id
})
if got do
[]
else
[Answer.new(query_id)]
end
end
end
end
end