From 3b1787c910c154983059e4565c73078f73a27595 Mon Sep 17 00:00:00 2001 From: guregu Date: Sat, 17 Aug 2024 09:35:36 +0900 Subject: [PATCH] add nicer example --- trealla/example_test.go | 6 +-- trealla/terms/example_test.go | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 trealla/terms/example_test.go diff --git a/trealla/example_test.go b/trealla/example_test.go index 239d910..5e15b42 100644 --- a/trealla/example_test.go +++ b/trealla/example_test.go @@ -130,7 +130,7 @@ func Example_register_nondet() { // Check Min and Max argument's type, must be integers (all integers are int64). min, ok := goal.Args[0].(int64) if !ok { - // throw(error(type_error(list, X), base32/2)). + // throw(error(type_error(integer, Min), betwixt/3)). yield(trealla.Atom("throw").Of(trealla.Atom("error").Of( trealla.Atom("type_error").Of("integer", goal.Args[0]), pi, @@ -141,9 +141,9 @@ func Example_register_nondet() { } max, ok := goal.Args[1].(int64) if !ok { - // throw(error(type_error(list, X), base32/2)). + // throw(error(type_error(integer, Max), betwixt/3)). yield(trealla.Atom("throw").Of(trealla.Atom("error").Of( - trealla.Atom("type_error").Of("integer", goal.Args[0]), + trealla.Atom("type_error").Of("integer", goal.Args[1]), pi, ))) return diff --git a/trealla/terms/example_test.go b/trealla/terms/example_test.go new file mode 100644 index 0000000..d715379 --- /dev/null +++ b/trealla/terms/example_test.go @@ -0,0 +1,75 @@ +package terms_test + +import ( + "context" + "fmt" + "iter" + + "github.com/trealla-prolog/go/trealla" + "github.com/trealla-prolog/go/trealla/terms" +) + +func Example_nondet_predicate() { + ctx := context.Background() + pl, err := trealla.New() + if err != nil { + panic(err) + } + + // Let's add a native equivalent of between/3. + // betwixt(+Min, +Max, ?N). + pl.RegisterNondet(ctx, "betwixt", 3, func(_ trealla.Prolog, _ trealla.Subquery, goal0 trealla.Term) iter.Seq[trealla.Term] { + return func(yield func(trealla.Term) bool) { + // goal is the goal called by Prolog, such as: base32("hello", X). + // Guaranteed to match up with the registered arity and name. + goal := goal0.(trealla.Compound) + + // Check Min and Max argument's type, must be integers (all integers are int64). + // TODO: should throw instantiation_error instead of type_error if Min or Max are variables. + min, ok := goal.Args[0].(int64) + if !ok { + // throw(error(type_error(integer, Min), betwixt/3)). + yield(terms.Throw(terms.TypeError("integer", goal.Args[0], terms.PI(goal)))) + return + } + max, ok := goal.Args[1].(int64) + if !ok { + // throw(error(type_error(integer, Max), betwixt/3)). + yield(terms.Throw(terms.TypeError("integer", goal.Args[1], terms.PI(goal)))) + return + } + + if min > max { + // Since we haven't yielded anything, this will fail. + return + } + + switch x := goal.Args[2].(type) { + case int64: + // If the 3rd argument is bound, we can do a simple check and stop iterating. + if x >= min && x <= max { + yield(goal) + return + } + case trealla.Variable: + // Create choice points unifying N from min to max + for n := min; n <= max; n++ { + goal.Args[2] = n + if !yield(goal) { + break + } + } + default: + yield(terms.Throw(terms.TypeError("integer", goal.Args[2], terms.PI(goal)))) + } + } + }) + + // Try it out. + answer, err := pl.QueryOnce(ctx, `findall(N, betwixt(1, 5, N), Ns), write(Ns).`) + if err != nil { + panic(err) + } + fmt.Println(answer.Stdout) + // Output: [1,2,3,4,5] +}