prolog
prolog copied to clipboard
convertAssignAny doesn't work for json([...]) Variables
I have found that sol.Scan doesn't serialize JSON Variables. The issue seems to come from the convertAssignAny method's case engine.Compound branch.
When I add this logging
if err := iter.Err(); err != nil {
fmt.Println("convertAssignAny compound iteration err", err)
return errConversion
}
I get this output
convertAssignAny compound iteration err error(type_error(list,json([=(id,someid),=(message,foobar)])),put_char/2)
Here is an example that hits this error
results(ResultJSON) :-
...
ResultJSON = json([
id = ResultID,
message = Message
]).
type Solution struct {
Result any `prolog:"ResultJSON"`
}
// Also fails with
// type Solution struct {
// Result struct {
// ID string `json:"id"`
// Message string `json:"message"`
// } `prolog:"ResultJSON"`
// }
sols, err := p.Query(`results(ResultJSON).`)
...
for sols.Next() {
var s Solution
if err := sols.Scan(&s); err != nil {
...
}
...
}
The Go code returns this error failed to run prolog eval: conversion failed from sols.Scan(&s)
Is there a way to get this Scan working? Or does does the library code need to change internally to support this?
Hi! This library has nothing to do with JSON. So, it doesn't know how to convert json(_), Key=Value, nor any other compound terms other than lists.
We can flatten the results so that we can address each field by a variable:
package main
import (
"encoding/json"
"fmt"
"github.com/ichiban/prolog"
)
func main() {
p := prolog.New(nil, nil)
if err := p.Exec(`
result(ID, Message) :-
results(json(KVs)),
member(id=ID, KVs),
member(message=Message, KVs).
results(json([id=someid, message=foobar])).
results(json([id=anotherid, message=hello])).
`); err != nil {
panic(err)
}
sols, err := p.Query(`result(ID, Message).`)
if err != nil {
panic(err)
}
for sols.Next() {
var s struct {
ID string `json:"id"`
Message string `json:"message"`
}
if err := sols.Scan(&s); err != nil {
panic(err)
}
b, err := json.Marshal(&s)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", string(b))
}
if err := sols.Err(); err != nil {
panic(err)
}
}
{"id":"someid","message":"foobar"}
{"id":"anotherid","message":"hello"}
Program exited.
https://go.dev/play/p/3VWBa-sz1b3
Alternatively, we can define our custom `prolog.Scanner` but it'll be unnecessarily hard:
package main
import (
"fmt"
"github.com/ichiban/prolog"
"github.com/ichiban/prolog/engine"
)
type JSONObject map[string]any
var _ prolog.Scanner = JSONObject{}
func (j JSONObject) Scan(vm *engine.VM, term engine.Term, env *engine.Env) error {
return nil // TODO: the hard work.
}
func main() {
p := prolog.New(nil, nil)
if err := p.Exec(`
results(json([id=someid, message=foobar])).
results(json([id=anotherid, message=hello])).
`); err != nil {
panic(err)
}
sols, err := p.Query(`results(ResultJSON).`)
if err != nil {
panic(err)
}
for sols.Next() {
var s struct {
ResultJSON JSONObject
}
if err := sols.Scan(&s); err != nil {
panic(err)
}
fmt.Printf("%#v\n", &s)
}
if err := sols.Err(); err != nil {
panic(err)
}
}