Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling Ciao Prolog from Elixir #96

Open
aherranz opened this issue Sep 16, 2024 · 4 comments
Open

Calling Ciao Prolog from Elixir #96

aherranz opened this issue Sep 16, 2024 · 4 comments

Comments

@aherranz
Copy link

Dear all we are planning to implement an Elixir library to interface Ciao Prolog programs. We need to load a Prolog/CLP program and (lazily) get every solution (including variable substitutions and/or constraints on them).

  • What are the options to implement the library (starting and interfacing an engine)?
  • What's the procedure to integrate the library in Ciao?
@aherranz
Copy link
Author

I am sandboxing by starting an engine and using stdin/stdout. Maybe this is enough for an initial version of our library. I am curious about the way the promt "?- " since I cannot read it from the stdout of the engine. Is the prompt emited through the stdout, is it directly emited in the tty, do you have other approach?

@jfmc
Copy link
Member

jfmc commented Sep 17, 2024

Hi @aherranz!

Yes, starting a new Ciao process and interacting via stdin/stdout is a good approach for simple query/answer solutions. Some example from bash:

$ query='append([1,2],[3,4],_X),writeq(_X).'; answer=$(echo "$query" | ciaosh -q); printf "query:%s answer:%s\n" "$query" "$answer"
query:append([1,2],[3,4],_X),writeq(_X). answer:[1,2,3,4]

Obviously this is really weak (see that we use _X so that the toplevel do not show the binding and shows the secondary prompt). A better approach is creating a small Ciao program that acts as a server, receiving messages (e.g., queries) and reacting (e.g., sending the solutions as an output). With some minor work, it is possible to compose the input and/or send the output in JSON (using library(json)) so facilitate the communication with other languages.

More or less, this is the approach that was taken in the Ciao Java interface, but using a more involved communication protocol. We did an experimental port of the same approach for a Python interface and it works fine, if serializing data for each query is not an issue.

Another approach would be a more tight interaction using the C interface (useful if you can preserve references to previously created terms and do a lot of queries). I have no experience with Erlang/Elixir but I guess that it'd possible to interface Elixir->C->Prolog in this way. I'd take this approach only when performance becomes a problem.

Please let us know if the server approach (using stdin/stdout) fits your application and we can send you some pointers to similar code in other interfaces.

@aherranz
Copy link
Author

Thank you!

As a prototype we will follow the most simple approach for us: we will start a Ciao engine from Elixir and we will feed it with queries and we will parse the answers directly reading and parsing the stdout of the engine. I'll keep you informed.

@aherranz
Copy link
Author

aherranz commented Sep 17, 2024

Let me show you our first session, I am using the low level functions that will not be public in the first protype. It seems that the stdin/out approach is promising:

iex(1)> alias Prolixir.Engine
Prolixir.Engine
iex(2)> engine = Engine.start "ciao", nil
%Porcelain.Process{pid: #PID<0.189.0>, out: {:send, #PID<0.188.0>}, err: :out}
iex(3)> Engine.read engine
{:ok, :out, "Ciao 1.23.0 [LINUXx86_64]\n"}
iex(4)> Engine.read engine
{:error, :empty}
iex(5)> Engine.write engine, "append(A,B,[1,2,3])"
{:input, "append(A,B,[1,2,3])"}
iex(6)> Engine.read engine
{:error, :empty}
iex(7)> Engine.write engine, ".\n"
{:input, ".\n"}
iex(8)> Engine.read engine
{:ok, :out, "\nA = [],\nB = [1,2,3] ? "}
iex(9)> Engine.write engine, ";\n"
{:input, ";\n"}
iex(10)> Engine.read engine
{:ok, :out, "\nA = [1],\nB = [2,3] ? "}
iex(11)> Engine.read engine
{:error, :empty}
iex(12)> Engine.write engine, ";\n"
{:input, ";\n"}
iex(13)> Engine.read engine
{:ok, :out, "\nA = [1,2],\nB = "}
iex(14)> Engine.write engine, ";\n"
{:input, ";\n"}
iex(15)> Engine.read engine
{:ok, :out, "[3] ? "}
iex(16)> Engine.write engine, ";\n"
{:input, ";\n"}
iex(17)> Engine.read engine
{:ok, :out, "\nA = [1,2,3],\nB = [] ? "}
iex(18)> Engine.read engine
{:ok, :out, "\nno"}
iex(19)> Engine.read engine
{:error, :empty}
iex(20)> Engine.write engine, "halt.\n"
{:input, "halt.\n"}
iex(21)> Engine.read engine
{:ok, :out, "\n"}
iex(22)> Engine.read engine
{:error, %Porcelain.Result{status: 0, out: {:send, #PID<0.188.0>}, err: :out}}
iex(23)> 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants