trealla-prolog / go

Trealla Prolog embedded in Go using WASM
MIT License
79 stars 3 forks source link
go logic-programming prolog trealla-prolog wasm

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

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]}

Struct compounds

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)

Documentation

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

Builtins

These additional predicates are built in:

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