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

engine.Repeat how to #346

Open
flashpixx opened this issue Mar 18, 2025 · 3 comments
Open

engine.Repeat how to #346

flashpixx opened this issue Mar 18, 2025 · 3 comments

Comments

@flashpixx
Copy link

Hello,

I try to add a database to my Prolog Go program, I'm following this idea with DynamoDB https://github.com/guregu/predicates/blob/master/dynamodb/dynamo.go#L66

But it seems that the engine.Repeat has been changed. How I can reach this with the current version? I have got a database with a lot of entries and I would like to iterate over each entry to and unify it for the return values.

Thanks a lot for help

@flashpixx flashpixx changed the title engine.Repeat engine.Repeat how to Mar 18, 2025
@guregu
Copy link
Contributor

guregu commented Mar 18, 2025

I really need to update that package, sorry about that. This discussion might be helpful: #163 (comment)
I think it was kind of a happy accident that it worked in the first place.

@flashpixx
Copy link
Author

flashpixx commented Mar 18, 2025

Thanks but for me it is not clear to adapt this. It could be helpful to put some minimal examples to the wiki, I try to implement a "Predicate7" e.g I would like to do "data(A,B,C,E,F,G)" on Prolog side and it should iterate over an existing database table element by element

@ichiban
Copy link
Owner

ichiban commented Mar 24, 2025

Hi, you can achieve it with recursion. The basic idea is described in #163 (comment) (Thanks, @guregu!).

Here's a detailed example that might work for you:

package main

import (
	"context"
	"database/sql"
	"log/slog"
	"os"

	"github.com/ichiban/prolog"
	"github.com/ichiban/prolog/engine"
	_ "modernc.org/sqlite"
)

type datum struct {
	A, B, C, D, E, F, G string
}

func main() {
	// Let's prepare an in-memory database with a table.
	db, err := sql.Open("sqlite", ":memory:")
	if err != nil {
		slog.With("err", err).Error("failed to open database")
		os.Exit(1)
	}
	if _, err := db.Exec(`CREATE TABLE data(a text, b text, c text, d text, e text, f text, g text);`); err != nil {
		slog.With("err", err).Error("failed to create table")
		os.Exit(1)
	}
	for _, d := range []datum{
		{A: "a", B: "b", C: "c", D: "d", E: "e", F: "f", G: "g"},
		{A: "h", B: "i", C: "j", D: "k", E: "l", F: "m", G: "n"},
		{A: "o", B: "p", C: "q", D: "r", E: "s", F: "t", G: "u"},
	} {
		if _, err := db.Exec(`INSERT INTO data(a, b, c, d, e, f, g) VALUES (?, ?, ?, ?, ?, ?, ?);`, d.A, d.B, d.C, d.D, d.E, d.F, d.G); err != nil {
			slog.With("err", err).Error("failed to insert data")
			os.Exit(1)
		}
	}

	p := prolog.New(nil, os.Stdout)

	// Now let's define the custom predicate data/7.
	p.Register7(engine.NewAtom("data"), func(vm *engine.VM, a, b, c, d, e, f, g engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
		// The first thing it does is to make a query and get rows.
		rows, err := db.Query(`SELECT * FROM data;`)
		if err != nil {
			return engine.Error(err)
		}

		// Then, it does `k` recursively.
		var k func(ctx context.Context) *engine.Promise
		k = func(ctx context.Context) *engine.Promise {
			// If the rows don't have any records left, it fails.
			if !rows.Next() {
				if err := rows.Close(); err != nil {
					return engine.Error(err)
				}
				return engine.Bool(false)
			}

			// If a record exists, it scans the record.
			var r datum
			if err := rows.Scan(&r.A, &r.B, &r.C, &r.D, &r.E, &r.F, &r.G); err != nil {
				return engine.Error(err)
			}

			// Then, it does either unify the record with the arguments or `k`.
			return engine.Delay(func(ctx context.Context) *engine.Promise {
				row := engine.NewAtom("row")
				output := row.Apply(a, b, c, d, e, f, g)
				input := row.Apply(engine.NewAtom(r.A), engine.NewAtom(r.B), engine.NewAtom(r.C), engine.NewAtom(r.D), engine.NewAtom(r.E), engine.NewAtom(r.F), engine.NewAtom(r.G))
				return engine.Unify(vm, output, input, cont, env)
			}, k)
		}
		return engine.Delay(k)
	})

	// Now you can call data/7.
	_ = p.QuerySolution(`data(A, B, C, D, E, F, G), write(row(A, B, C, D, E, F, G)), write(.), nl, fail.`).Err()
}
row(a,b,c,d,e,f,g).
row(h,i,j,k,l,m,n).
row(o,p,q,r,s,t,u).

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

3 participants