github.com/trealla-prolog/trealla-go

Trealla Prolog embedded in Go using WASM


Keywords
go, logic-programming, prolog, trealla-prolog, wasm
License
MIT
Install
go get github.com/trealla-prolog/trealla-go

Documentation

trealla-go GoDoc

import "github.com/trealla-prolog/go/trealla"

Prolog interface for Go using Trealla Prolog and wazero. It's pretty fast. Not as fast as native Trealla, but pretty dang fast (about 2x slower than native).

Development Status: inching closer to stability

Caveats

  • Beta status, API will probably change
    • API is relatively stable now.

Install

go get github.com/trealla-prolog/go

Note: the module is under github.com/trealla-prolog/go, not [...]/go/trealla. go.dev is confused about this and will pull a very old version if you try to go get the trealla package.

Usage

This library uses WebAssembly to run Trealla, executing Prolog queries in an isolated environment.

import "github.com/trealla-prolog/go/trealla"

func main() {
	// load the interpreter and (optionally) grant access to the current directory
	pl := trealla.New(trealla.WithPreopen("."))
	// run a query; cancel context to abort it
	ctx := context.Background()
	query := pl.Query(ctx, "member(X, [1, foo(bar), c]).")

	// calling Close is not necessary if you iterate through the whole result set
	// but it doesn't hurt either
	defer query.Close()

	// iterate through answers
	for query.Next(ctx) {
		answer := query.Current()
		x := answer.Solution["X"]
		fmt.Println(x) // 1, trealla.Compound{Functor: "foo", Args: [trealla.Atom("bar")]}, "c"
	}

	// make sure to check the query for errors
	if err := query.Err(); err != nil {
		panic(err)
	}
}

Single query

Use QueryOnce when you only want a single answer.

pl := trealla.New()
answer, err := pl.QueryOnce(ctx, "succ(41, N).")
if err != nil {
	panic(err)
}

fmt.Println(answer.Stdout)
// Output: hello world

Binding variables

You can bind variables in the query using the WithBind and WithBinding options. This is a safe and convenient way to pass data into the query. It is OK to pass these multiple times.

pl := trealla.New()
answer, err := pl.QueryOnce(ctx, "write(X)", trealla.WithBind("X", trealla.Atom("hello world")))
if err != nil {
	panic(err)
}

fmt.Println(answer.Stdout)
// Output: hello world

Scanning solutions

You can scan an answer's substitutions directly into a struct or map, similar to ichiban/prolog.

Use the prolog:"VariableName" struct tag to manually specify a variable name. Otherwise, the field's name is used.

answer, err := pl.QueryOnce(ctx, `X = 123, Y = abc, Z = ["hello", "world"].`)
if err != nil {
	panic(err)
}

var result struct {
	X  int
	Y  string
	Hi []string `prolog:"Z"`
}
// make sure to pass a pointer to the struct!
if err := answer.Solution.Scan(&result); err != nil {
	panic(err)
}

fmt.Printf("%+v", result)
// Output: {X:123 Y:abc Hi:[hello world]}

Documentation

See package trealla's documentation for more details and examples.

Builtins

These additional predicates are built in:

  • crypto_data_hash/3
  • http_consult/1
    • Argument can be URL string, or my_module_name:"https://url.example"

WASM binary

This library embeds the Trealla WebAssembly binary in itself, so you can use it without any external dependencies. The binaries are currently sourced from guregu/trealla.

Building the WASM binary

If you'd like to build libtpl.wasm yourself:

# The submodule in src/trealla points to the current version
git submodule update --init --recursive
# Use the Makefile in the root of this project
make clean wasm
# Run the tests and benchmark to make sure it works
go test -v ./trealla -bench=.

Thanks

License

MIT. See ATTRIBUTION as well.

See also