Absent values
A field the source didn't state is absent, which isn't the same as empty, and how to ask about one.
A source can leave a field out. A datasheet row might state a maximum and no minimum, and a package might declare no license. jaala keeps that apart from a field stated as the empty string, because “not stated” and “stated as nothing” are different facts, and a query that mixed them up would answer the wrong question without saying so. As a host, you serve an absent field as ns.Absent(), which is a bit more work than an empty string.
package guide_test
import (
"context"
"fmt"
"github.com/panyam/jaala/datalog"
"github.com/panyam/jaala/ns"
)
// An absent value is a field the source didn't state. It sorts before
// everything else, it isn't the same as "", and absent(?x) asks about it.
func Example_absent() {
b, ctx := base(), context.Background()
for _, text := range []string{
`pkg(?p, ?l) => ?l, ?p`,
`pkg(?p, ?l), absent(?l) => ?p`,
`pkg(?p, ?l), not absent(?l) => ?p`,
`pkg(?p, "") => ?p`,
} {
rows, err := datalog.SemiNaive{}.Eval(ctx, datalog.MustParse(text), b)
if err != nil {
fmt.Println(err)
continue
}
fmt.Println(text)
for _, r := range rows {
if l, ok := r.Bind["l"]; ok {
fmt.Println(" ", r.Bind["p"].S, show(l))
} else {
fmt.Println(" ", r.Bind["p"].S)
}
}
}
// Output:
// pkg(?p, ?l) => ?l, ?p
// api absent
// log ""
// util "Apache-2.0"
// app "MIT"
// pkg(?p, ?l), absent(?l) => ?p
// api
// pkg(?p, ?l), not absent(?l) => ?p
// app
// log
// util
// pkg(?p, "") => ?p
// log
}
// show prints a value as a host might: quoted text, or "absent".
func show(v ns.Value) string {
if v.Absent {
return "absent"
}
return fmt.Sprintf("%q", v.S)
}
How an absent value behaves ΒΆ
- It sorts before every number and every piece of text, so in the default order absent rows come first.
- It isn’t equal to
"".pkg(?p, "")findslog, which states an empty license, and notapi, which states none. Grouping keeps them apart too. absent(?x)holds for an absent value and nothing else, so it’s the way to ask for one.not absent(?x)reads “this field is stated”, which you’ll probably want about as often.ValidateBoundchecks a query before the host has the values it will bind. It stands an absent value in for each one, which neither the type check nor a closed set of allowed values refuses, so the check judges the query and not a placeholder.