Skip to content

Resolver trait #37

@dcz-self

Description

@dcz-self

I've been trying to create a resolver for arithmetics and I found the interface of the trait to be somewhat confusing.

Here's the trait for reference:

pub trait Resolver {
    type Choice: fmt::Debug;

    fn resolve(
        &mut self,
        goal_id: term_arena::TermId,
        goal_term: term_arena::AppTerm,
        context: &mut ResolveContext,
    ) -> Option<Resolved<Self::Choice>>;

    /// Returns true if there's another resolution pending.
    fn resume(
        &mut self,
        choice: &mut Self::Choice,
        goal_id: term_arena::TermId,
        context: &mut ResolveContext,
    ) -> bool;
}

As far as I undestand it, the resolver exists to return a chain of results. Every result can either just match, which means "the statement is true", or instantiate a variable to some concrete value, and let the resolution process continue elsewhere. The first lack of match breaks the chain.
Did I get that right?

If yes, then Resolver::Choice reminds me of an Iterator a lot. Without getting into how all this works, I'd imagine returning an Iterator that yields which variable should take which value or stop, something along the lines of:

enum Choice {
 Substitute(Var, Term),
 Match,
}

pub trait Resolver {
    fn resolve(
        &mut self,
        goal_id: term_arena::TermId,
        goal_term: term_arena::AppTerm,
        context: &mut ResolveContext,
    ) -> impl Iterator<Item=Choice>;
}

If that doesn't make sense, I'm seeing a couple of less invasive rough edges with this API.

One simple thing is that ::resume returns a bool where it could return another choice:

/// Returning None means nothing more can be resolved.
fn resume(
        &mut self,
        choice: Self::Choice,
        goal_id: term_arena::TermId,
        context: &mut ResolveContext,
    ) -> Option<Self::Choice>;

Another thing is that Resolver::resolve duplicates ::resume when it returns the first result (e.g. in RuleResolver). I think this could be avoided by calling ::resume first thing after each ::resolve.
When I tried it, it becomes clear that while the normal place to fail and stop resolution is ::resume, sometimes ::resolve already wants to fail immediately, so that would lead to some weird overlap of function.
I decided not to make a pull request from what I have without confirming that it's even desireable.


This all ultimately comes from the need to have a CLP/FD approximation :P Scryer Prolog has some downsides compared to Logru.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions