Current section
Files
Jump to
Current section
Files
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