To draw inferences from the relations created explicitly by the source text.


§1. Managing the BPs generated. The relations created in this section belong to the "explicit" family, named so because their definitions are explicit in the source text. Initially, there are none.

bp_family *explicit_bp_family = NULL;
bp_family *by_function_bp_family = NULL;
typedef struct explicit_bp_data {
    int form_of_relation;  one of the Relation_* constants defined below
    struct property *i6_storage_property;  provides run-time storage
    struct equivalence_bp_data *equiv_data;  only used for Relation_Equiv
    struct inter_name *v2v_bitmap_iname;  only used for Relation_VtoV and Relation_Sym_VtoV
    int store_dynamically;
    CLASS_DEFINITION
} explicit_bp_data;

§2.

void ExplicitRelations::start(void) {
    explicit_bp_family = BinaryPredicateFamilies::new();
    METHOD_ADD(explicit_bp_family, TYPECHECK_BPF_MTID, ExplicitRelations::typecheck);
    METHOD_ADD(explicit_bp_family, ASSERT_BPF_MTID, ExplicitRelations::assert);
    METHOD_ADD(explicit_bp_family, SCHEMA_BPF_MTID, ExplicitRelations::schema);
    METHOD_ADD(explicit_bp_family, DESCRIBE_FOR_PROBLEMS_BPF_MTID, ExplicitRelations::describe_for_problems);
    METHOD_ADD(explicit_bp_family, DESCRIBE_FOR_INDEX_BPF_MTID, ExplicitRelations::describe_for_index);
    by_function_bp_family = BinaryPredicateFamilies::new();
    METHOD_ADD(by_function_bp_family, TYPECHECK_BPF_MTID, ExplicitRelations::typecheck);
    METHOD_ADD(by_function_bp_family, ASSERT_BPF_MTID, ExplicitRelations::assert);
    METHOD_ADD(by_function_bp_family, SCHEMA_BPF_MTID, ExplicitRelations::schema);
    METHOD_ADD(by_function_bp_family, DESCRIBE_FOR_PROBLEMS_BPF_MTID, ExplicitRelations::describe_for_problems);
    METHOD_ADD(by_function_bp_family, DESCRIBE_FOR_INDEX_BPF_MTID, ExplicitRelations::REL_br_describe_briefly);
}

int ExplicitRelations::is_explicit_with_runtime_storage(binary_predicate *bp) {
    if (bp->relation_family == explicit_bp_family) return TRUE;
    return TRUE;
}

§3. The following constants are numbered in a way which corresponds to some run-time code supporting relations.

define Relation_Implicit   -1  used to mean "none of the below"
define Relation_OtoO       1  one to one: "R relates one K to one K"
define Relation_OtoV       2  one to various: "R relates one K to various K"
define Relation_VtoO       3  various to one: "R relates various K to one K"
define Relation_VtoV       4  various to various: "R relates various K to various K"
define Relation_Sym_OtoO   5  symmetric one to one: "R relates one K to another"
define Relation_Sym_VtoV   6  symmetric various to various: "R relates K to each other"
define Relation_Equiv      7  equivalence relation: "R relates K to each other in groups"

§4.

int ExplicitRelations::allow_arbitrary_assertions(binary_predicate *bp) {
    int f = ExplicitRelations::get_form_of_relation(bp);
    if (f == Relation_Equiv) return TRUE;
    if (f == Relation_VtoV) return TRUE;
    if (f == Relation_Sym_VtoV) return TRUE;
    return FALSE;
}

void ExplicitRelations::store_dynamically(binary_predicate *bp) {
    if (bp->relation_family == explicit_bp_family) {
        explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
        ED->store_dynamically = TRUE;
    } else internal_error("not explicit");
}

int ExplicitRelations::stored_dynamically(binary_predicate *bp) {
    if (bp->relation_family == explicit_bp_family) {
        explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
        return ED->store_dynamically;
    }
    return FALSE;
}

int ExplicitRelations::relates_values_not_objects(binary_predicate *bp) {
    if (bp->relation_family == explicit_bp_family) {
        kind *K = BinaryPredicates::kind(bp);
        kind *K0, *K1;
        Kinds::binary_construction_material(K, &K0, &K1);
        if ((Kinds::Behaviour::is_object(K0)) && (Kinds::Behaviour::is_object(K1)))
            return FALSE;
        return TRUE;
    }
    return FALSE;
}

§5. When the source text declares new relations, it turns out to be convenient to make their BPs in a two-stage process: to make sketchy, mostly-blank BP structures for them early on — but getting their names registered — and then fill in the correct details later. This is where such sketchy pairs are made:

binary_predicate *ExplicitRelations::make_pair_sketchily(word_assemblage wa) {
    TEMPORARY_TEXT(relname)
    WRITE_TO(relname, "%V", WordAssemblages::first_word(&wa));
    binary_predicate *bp =
        BinaryPredicates::make_pair(explicit_bp_family,
        BPTerms::new(NULL), BPTerms::new(NULL),
        relname, NULL, NULL, NULL, wa);
    DISCARD_TEXT(relname)
    explicit_bp_data *ED = CREATE(explicit_bp_data);
    bp->family_specific = STORE_POINTER_explicit_bp_data(ED);
    bp->reversal->family_specific = STORE_POINTER_explicit_bp_data(ED);

    ED->equiv_data = NULL;
    ED->i6_storage_property = NULL;
    ED->form_of_relation = Relation_OtoO;
    ED->v2v_bitmap_iname = NULL;
    ED->store_dynamically = FALSE;

    return bp;
}

property *ExplicitRelations::get_i6_storage_property(binary_predicate *bp) {
    if (bp->relation_family != explicit_bp_family) return NULL;
    explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
    return ED->i6_storage_property;
}

int ExplicitRelations::get_form_of_relation(binary_predicate *bp) {
    if (bp->relation_family != explicit_bp_family) return Relation_Implicit;
    explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
    return ED->form_of_relation;
}
char *ExplicitRelations::form_to_text(binary_predicate *bp) {
    switch(ExplicitRelations::get_form_of_relation(bp)) {
        case Relation_OtoO: return "Relation_OtoO";
        case Relation_OtoV: return "Relation_OtoV";
        case Relation_VtoO: return "Relation_VtoO";
        case Relation_VtoV: return "Relation_VtoV";
        case Relation_Sym_OtoO: return "Relation_Sym_OtoO";
        case Relation_Sym_VtoV: return "Relation_Sym_VtoV";
        case Relation_Equiv: return "Relation_Equiv";
        default: return "Relation_Implicit";
    }
}

§6. They typecheck by the default rule only:

int ExplicitRelations::typecheck(bp_family *self, binary_predicate *bp,
        kind **kinds_of_terms, kind **kinds_required, tc_problem_kit *tck) {
    return DECLINE_TO_MATCH;
}

§7. They are asserted thus. Note that if we have a symmetric relation then we need to behave as if \(B(y, x)\) had also been asserted whenever \(B(x, y)\) has, if \(x\neq y\).

int ExplicitRelations::assert(bp_family *self, binary_predicate *bp,
        inference_subject *infs0, parse_node *spec0,
        inference_subject *infs1, parse_node *spec1) {

    Reject non-assertable relations7.1;
    if (ExplicitRelations::stored_dynamically(bp)) {
        RelationInferences::draw_spec(bp, spec0, spec1);
        return TRUE;
    } else {
        if ((infs0 == NULL) || (infs1 == NULL)) Reject relationship with nothing7.2;
        if (ExplicitRelations::allow_arbitrary_assertions(bp)) {
            RelationInferences::draw(bp, infs0, infs1);
            if ((ExplicitRelations::get_form_of_relation(bp) == Relation_Sym_VtoV) && (infs0 != infs1))
                RelationInferences::draw(bp, infs1, infs0);
            return TRUE;
        }
        if (ExplicitRelations::is_explicit_with_runtime_storage(bp)) {
            ExplicitRelations::infer_property_based_relation(bp, infs1, infs0);
            if ((ExplicitRelations::get_form_of_relation(bp) == Relation_Sym_OtoO) && (infs0 != infs1))
                ExplicitRelations::infer_property_based_relation(bp, infs0, infs1);
            return TRUE;
        }
    }
    return FALSE;
}

§7.1. This is the point at which non-assertable relations are thrown out.

Reject non-assertable relations7.1 =

    if (BinaryPredicates::can_be_made_true_at_runtime(bp) == FALSE) {
        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_Unassertable2),
            "the relationship you describe is not exact enough",
            "so that I do not know how to make this assertion come true. "
            "For instance, saying 'The Study is adjacent to the Hallway.' "
            "is not good enough because I need to know in what direction: "
            "is it east of the Hallway, perhaps, or west?");
        return TRUE;
    }

§7.2. Reject relationship with nothing7.2 =

    StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_CantRelateNothing),
        "the relationship you describe seems to be with nothing",
        "which does not really make sense. 'Nothing' looks like a noun, "
        "but really Inform uses it to mean the absence of one, so it's "
        "against the rules to say something like 'Mr Cogito disputes nothing' "
        "to try to put 'Mr Cogito' and 'nothing' into a relationship.");
    return TRUE;

§8. This routine converts the knowledge that \(R(ox, oy)\) into a single inference. It can only be used for a simple subclass of the relations: those which store oy, the only thing related to ox, in a given property of ox. The beauty of this is that the "only thing related to" business is then enforced by the inference mechanism, since an attempt to assert both \(R(x,y)\) and \(R(x,z)\) will result in contradictory property value inferences for \(y\) and \(z\).

void ExplicitRelations::infer_property_based_relation(binary_predicate *bp,
    inference_subject *infs0, inference_subject *infs1) {
    if (ExplicitRelations::get_form_of_relation(bp) == Relation_VtoO) {
        inference_subject *swap=infs0; infs0=infs1; infs1=swap;
    }
    property *prn = ExplicitRelations::get_i6_storage_property(bp);
    PropertyInferences::draw(infs0, prn, InferenceSubjects::as_constant(infs1));
}

§9. We need do nothing special: these relations can be compiled from their schemas.

int ExplicitRelations::schema(bp_family *self, int task, binary_predicate *bp, annotated_i6_schema *asch) {
    return FALSE;
}

§10. Problem message text:

int ExplicitRelations::describe_for_problems(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
    return FALSE;
}
void ExplicitRelations::describe_for_index(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
    switch (ExplicitRelations::get_form_of_relation(bp)) {
        case Relation_OtoO: WRITE("one-to-one"); break;
        case Relation_OtoV: WRITE("one-to-various"); break;
        case Relation_VtoO: WRITE("various-to-one"); break;
        case Relation_VtoV: WRITE("various-to-various"); break;
        case Relation_Sym_OtoO: WRITE("one-to-another"); break;
        case Relation_Sym_VtoV: WRITE("various-to-each-other"); break;
        case Relation_Equiv: WRITE("in groups"); break;
    }
}
void ExplicitRelations::REL_br_describe_briefly(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
    WRITE("defined");
}