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
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.
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)
}
}
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
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
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]}
Prolog compounds can destructure into Go structs. A special field of type trealla.Functor
will be set to the functor.
The compound's arguments are matched with the exported struct fields in order.
These structs can also be used to bind variables in queries.
?- findall(kv(Flag, Value), current_prolog_flag(Flag, Value), Flags).
Flags = [kv(double_quotes,chars),kv(char_conversion,off),kv(occurs_check,false),kv(character_escapes,true),...]
// kv(Flag, Value)
type KV struct {
trealla.Functor `prolog:"kv/2"` // tag is optional, but can be used to specify the functor/arity
Flag trealla.Atom // 1st arg
Value trealla.Term // 2nd arg
}
var result struct {
Flags []KV // Flags variable
}
answer.Solution.Scan(&result)
See package trealla's documentation for more details and examples.
These additional predicates are built in:
crypto_data_hash/3
http_consult/1
my_module_name:"https://url.example"
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.
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=.
MIT. See ATTRIBUTION as well.