A DSL to write logic programs in Elixir
Add :logic_elixir to the list of dependencies in mix.exs:
def deps do
[
{:logic_elixir, git: "git@github.com:MiguelERuiz/logic_elixir.git", tag: "0.1.0"}
]
endAfter that, run mix deps.get and mix compile
Declare the use LogicElixir expression inside your module, as below:
defmodule LogicModule do
use LogicElixir
defpred p(5)
endThis version supports a reduced set of Elixir terms and must be used as follows:
LogicElixir.t() :: integer()
| atom()
| float()
| tuple()
| [t()]Keep in mind that uppercase atoms such as X, Character, etc. are used to
represent logic variables.
LogicElixir offers defpred macro to declare your logic predicates, and
supports both declaring logic facts and rules, as the following examples:
defmodule MiddleEarth do
use LogicElixir
# Facts
defpred hobbit(:frodo)
defpred hobbit(:sam)
defpred hobbit(:bilbo)
defpred wizard(:gandalf)
defpred wizard(:saruman)
defpred elf(:legolas)
# Rules
defpred fellow(X) do
# To make a disjunction of goals, use `choice` operator
choice do
X = :frodo # Unification of LogicElixir terms
else
X = :sam
else
X = :legolas
else
X = :gandalf
end
end
# Predicates to operate with data structures
defpred append([], Ys, Ys)
defpred append([X|Xs], Ys, [X|Zs]) do
append(Xs, Ys, Zs)
end
defpred is_ordered([])
defpred is_ordered([X | []])
defpred is_ordered([X | [Y | Ys]]) do
@(X <= Y) # Elixir expression evaluation with @ operator
is_ordered([Y | Ys])
end
endA full repository with examples can be found here
LogicElixir offers findall macro to make queries of your logic predicates.
This macro receives three arguments:
- A
LogicElixirterm - A
LogicElixirgoal sequence - An optional
Enumerableterm to get the results of the query
findall outputs a Stream with the solution of the query or the result inside
the optional Enumerable term. Here there are examples of declaring this macro:
findall Hobbit, do: hobbit(Hobbit) # without optional Enumerable term
findall X do
hobbit(X)
fellow(X)
end
findall X, into: [], do: (hobbit(X) ; fellow(X))
findall Hobbit, into: [], do: hobbit(Hobbit)It's useful to encapsulate this macro inside an Elixir function to get the results, as the following examples:
defmodule MiddleEarth do
use LogicElixir
# ...
def hobbits do
findall Hobbit, do: hobbit(Hobbit)
end
endIf you open an iex -S mix session, you can get the result:
iex(1)> MiddleEarth.hobbits
#Stream<[
enum: #Function<60.124013645/2 in Stream.transform/3>,
funs: [#Function<48.124013645/1 in Stream.map/2>]
]>
iex(2)> MiddleEarth.hobbits |> Enum.take(1)
[:frodo]Besides, you can declare Elixir functions that receive arguments and use it inside:
defmodule LogicLists do
use LogicElixir
defpred append([], Ys, Ys)
defpred append([X|Xs], Ys, [X|Zs]) do
append(Xs, Ys, Zs)
end
def pairs_of_lists(x) do
(findall {X, Y}, into: [], do: append(X, Y, x))
end
endAgain, you can test it in your Elixir shell:
iex(1)> LogicLists.pairs_of_lists([1,2,3,4,5])
[
{[], [1, 2, 3, 4, 5]},
{[1], [2, 3, 4, 5]},
{[1, 2], [3, 4, 5]},
{[1, 2, 3], [4, 5]},
{[1, 2, 3, 4], [5]},
{[1, 2, 3, 4, 5], []}
]