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 one_of.ex
Raw

lib/guesswork/ast/one_of.ex

defmodule Guesswork.Ast.OneOf do
@moduledoc """
Binds a value to an `Enumerable` of possible values.
Note that, when created, the bound value should always be a variable.
However, in the process of substitution it may become a concrete value, in
which case, on resolution, the stored value is checked against the stream.
It is invalid for the stream to produce a variable.
"""
defstruct [:binding, :values]
alias Guesswork.Answer
alias Guesswork.Ast.Entity
alias Guesswork.Ast.Statement.Opts
alias Guesswork.Ast.Variable
alias Guesswork.Telemetry.EventHandler
@type t() :: %__MODULE__{binding: Variable.t() | Entity.t(), values: Enumerable.t()}
@spec new(Variable.t(), Enumerable.t()) :: t()
def new(variable, stream), do: %__MODULE__{binding: variable, values: stream}
defimpl Guesswork.Ast.Term do
alias Guesswork.Ast.OneOf
def concrete?(%OneOf{binding: %Variable{}}), do: false
def concrete?(_), do: true
def get_variables(%OneOf{binding: %Variable{} = variable}), do: [variable]
def get_variables(_), do: []
end
defimpl Guesswork.Ast.Statement do
alias Guesswork.Ast.OneOf
def find_statements(statement, fun) do
if fun.(statement) do
[statement]
else
[]
end
end
def simplify(statement) do
statement
end
def substitute(%OneOf{binding: binding} = statement, env) do
case Map.fetch(env, binding) do
:error -> statement
{:ok, value} -> %OneOf{statement | binding: value}
end
end
def resolve(
%OneOf{binding: %Variable{} = var, values: stream},
%Opts{query_id: query_id, negated: negated}
) do
stream
|> Stream.filter(fn
%Variable{} -> false
_ -> true
end)
|> Stream.map(fn val ->
:telemetry.execute(EventHandler.assignment_event_name(), %{count: 1}, %{
query_id: query_id
})
{:ok, answer} = Answer.new(query_id) |> Answer.put_binding(var, val, negated)
answer
end)
end
def resolve(%OneOf{binding: value, values: stream}, %Opts{
query_id: query_id,
negated: negated
}) do
stream
|> Stream.filter(fn
%Variable{} -> false
val -> val == value != negated
end)
|> Stream.map(fn _ ->
:telemetry.execute(EventHandler.assignment_event_name(), %{count: 1}, %{
query_id: query_id
})
Answer.new(query_id)
end)
end
end
end