Skip to content

Predicate Selection

Predicates are the fundamental building blocks of Mistaber encodings. Choosing the correct predicate ensures type safety, semantic clarity, and compatibility with the reasoning engine. This document explains predicate naming conventions, the registry reference, arity guidelines, and when to create new predicates.

Predicate Naming Conventions

General Naming Rules

Rule Example Rationale
All lowercase food_type ASP convention
Underscores for separation is_beheima_chalav_mixture Readability
Descriptive names mixture_has_basar Self-documenting
No abbreviations (usually) permitted not perm Clarity
Hebrew terms when appropriate achiila, chalav Domain accuracy

Rule ID Naming

Rule identifiers use the r_ prefix:

% CORRECT - descriptive, topic-based
r_bb_beheima_achiila      % basar bechalav beheima eating
r_bb_of_bishul_mutar      % basar bechalav of cooking permitted
r_bb_dag_sakana           % fish danger in bb laws
r_rema_dag_chalav_mutar   % Rema's fish+dairy permission

% WRONG - opaque, location-based
r_87_1                    % What is this?
r_rule_001                % Meaningless
r_yd87s3                  % Cryptic

Category Naming

Category Prefix/Pattern Example
Sort membership is_X is_food, is_mixture
Classification X_type food_type, kli_status
Relationships contains, has_X contains, has_issur
Actions verb form achiila, bishul
Status issur, heter issur(achiila, X, madrega)

Existing Predicate Registry Reference

Core Sort Predicates

Predicate Arity Signature CWA Description
food 1 [food] Yes Entity is food
mixture 1 [mixture] Yes Entity is mixture
vessel 1 [vessel] Yes Entity is vessel
world 1 [world] Yes Valid world identifier
rule 1 [rule_id] Yes Valid rule identifier

Food Classification

Predicate Arity Signature CWA Description
food_type 2 [food, food_category] Yes Food's category
basar_type 1 [category] Yes Subcategory of basar
is_basar_type 1 [category] No Helper predicate

Mixture Predicates

Predicate Arity Signature CWA Description
contains 2 [mixture, food] Yes Mixture contains food
mixture_has_basar 2 [mixture, basar_type] No Derived: has meat type
mixture_has_chalav 1 [mixture] No Derived: has dairy
is_beheima_chalav_mixture 1 [mixture] No Derived: beheima+dairy
is_of_chalav_mixture 1 [mixture] No Derived: poultry+dairy
is_dag_chalav_mixture 1 [mixture] No Derived: fish+dairy

Normative Predicates

Predicate Arity Signature CWA Description
issur 3 [action, food, madrega] No Prohibition with level
heter 2 [action, food] No Permission
sakana 1 [food] No Health danger
no_sakana 1 [food] No No health danger
forbidden 4 [world, action, food, context] No Full prohibition
permitted 4 [world, action, food, context] No Full permission

Attribution Predicates

Predicate Arity Signature CWA Description
makor 2 [rule_id, source_ref] Yes Source citation
madrega 2 [rule_id, madrega_type] Yes Obligation level
scope 2 [rule_id, world] Yes World scope

World Structure

Predicate Arity Signature CWA Description
accessible 2 [world, world] Yes Parent relationship
asserts 2 [world, proposition] Yes World asserts proposition
holds 2 [proposition, world] No Proposition holds in world
override 3 [world, prop, value] Yes World overrides proposition
overridden 2 [proposition, world] No Proposition is overridden

Enumerated Values

action_type: achiila, bishul, hanaah, melicha, kavush, netila

food_category: basar, chalav, parve, beheima, chaya, of, dag, mashkeh, tavlin

madrega_type: d_oraita, d_rabanan, minhag, chumra

context: ctx_normal, ctx_hefsed, ctx_shaat_hadchak, ctx_choleh, ctx_pikuach_nefesh

When to Create New Predicates

Decision Tree

graph TD
    A[Need to express something] --> B{Existing predicate?}

    B -->|Yes| C[Use existing predicate]
    B -->|No| D{Related predicate exists?}

    D -->|Yes| E{Can extend existing?}
    D -->|No| F{Is it domain-specific?}

    E -->|Yes| G[Create helper predicate]
    E -->|No| H[Create new predicate]

    F -->|Yes| H
    F -->|No| I[Consider if needed]

    H --> J[Document in PR]
    G --> J

Valid Reasons to Create New Predicates

  1. New domain concept not covered by existing predicates
  2. Helper predicate for complex derived conditions
  3. Specific mixture type detection (pattern: is_X_Y_mixture)
  4. New action type (must be added to enum)

Invalid Reasons to Create New Predicates

  1. Synonym of existing - use existing predicate
  2. Slightly different arity - consider if existing works
  3. Convenience only - helpers should serve multiple rules
  4. Personal preference - follow established patterns

Predicate Arity Guidelines

Arity 1 (Unary)

Use for sort membership and simple properties:

% Sort membership
food(chicken).
mixture(m1).
vessel(pot1).

% Simple properties
sakana(M).
no_sakana(M).

Arity 2 (Binary)

Use for classifications, relationships, and simple assertions:

% Classifications
food_type(chicken, of).
kli_status(pot1, kli_rishon).

% Relationships
contains(m1, chicken).
accessible(ashk_mb, rema).

% Assertions
makor(r_example, sa("yd:87:1")).
madrega(r_example, d_oraita).

Arity 3 (Ternary)

Use for actions with context or propositions with conditions:

% Action with level
issur(achiila, M, d_oraita).
issur(bishul, M, d_rabanan).

% Override with value
override(rema, sakana(M), no_sakana).

Arity 4 (Quaternary)

Use for full normative determinations:

% Full prohibition structure
forbidden(world, action_type, food, context).
forbidden(mechaber, achiila, M, ctx_normal).

% Full permission structure
permitted(world, action_type, food, context).
permitted(rema, achiila, M, ctx_normal).

Arity Guidelines Summary

Arity Use Case Example
1 Membership, simple property food(X), sakana(M)
2 Classification, binary relation food_type(X, Y), contains(M, F)
3 Action + qualifier, override issur(A, F, M), override(W, P, V)
4 Full determination with context forbidden(W, A, F, C)
5+ Rarely needed, consider decomposition Avoid if possible

Type Constraints

Sort Compatibility

Every predicate argument must match its declared sort:

% food_type/2 has signature [food, food_category]

% CORRECT - arguments match sorts
food_type(chicken, of).        % chicken: food, of: food_category
food_type(milk, chalav).       % milk: food, chalav: food_category

% WRONG - sort mismatch
food_type(pot1, basar).        % pot1 is vessel, not food!
food_type(chicken, d_oraita).  % d_oraita is madrega, not food_category!

Checking Sort Compatibility

Before using a predicate:

  1. Check the predicate registry for signature
  2. Verify each argument matches its declared sort
  3. Ensure enum values are from correct enumeration
% Registry shows: issur/3 = [action_type, food, madrega_type]

% Verify each argument:
% - achiila: action_type enum? YES
% - M: mixture/food? YES (mixture is subtype)
% - d_oraita: madrega_type enum? YES

issur(achiila, M, d_oraita).  % Valid!

CWA vs OWA Predicates

CWA (Closed World Assumption): If not stated, it's false.

% CWA predicates - absence = false
food(chicken).      % chicken is food
% beef is NOT food (not declared)

OWA (Open World Assumption): Absence doesn't imply falsity.

% OWA predicates - absence = unknown
permitted(W, A, F, C).  % derived, not directly stated
forbidden(W, A, F, C).  % derived, not directly stated

Negating OWA Predicates

Negating open-world predicates is unsafe and generates a warning:

% DANGEROUS - negating OWA
allowed(F) :- food(F), not forbidden(_, _, F, _).
% TypeCheckWarning: Using negation with OWA predicate 'forbidden'

Creating New Predicates

Step 1: Verify Necessity

  • [ ] No existing predicate serves this purpose
  • [ ] Cannot compose from existing predicates
  • [ ] Will be used by multiple rules (not one-off)
  • [ ] Has clear semantic meaning

Step 2: Design the Predicate

# Proposed predicate documentation
- name: is_X_Y_mixture
  arity: 1
  signature: [mixture]
  hebrew: "תערובת X עם Y"
  english: "Mixture containing X and Y"
  cwa: false
  derived: true
  note: "Derived predicate for detecting X+Y combinations"

Step 3: Implement with Documentation

% ============================================================
% NEW PREDICATE: is_X_Y_mixture/1
% ============================================================
% Purpose: Detects mixtures containing both X and Y
% Signature: [mixture] -> boolean
% CWA: false (derived)
%
% Usage:
%   is_X_Y_mixture(M) :- mixture(M), contains(M, X_item), contains(M, Y_item).
%
% Added: 2026-01-25
% Change: Record the PR/reference that introduced this predicate.
% ============================================================

is_X_Y_mixture(M) :-
    mixture(M),
    contains(M, F1),
    food(F1),
    food_type(F1, x_category),
    contains(M, F2),
    food(F2),
    food_type(F2, y_category),
    F1 != F2.

Step 4: Add Tests

def test_new_predicate_positive():
    """New predicate correctly detects X+Y mixture."""
    result = query("""
        mixture(m1).
        food(x_item). food_type(x_item, x_category).
        food(y_item). food_type(y_item, y_category).
        contains(m1, x_item). contains(m1, y_item).
    """)
    assert result.holds("is_X_Y_mixture(m1)")

def test_new_predicate_negative():
    """New predicate does not fire for non-matching mixtures."""
    result = query("""
        mixture(m1).
        food(x_item). food_type(x_item, x_category).
        food(z_item). food_type(z_item, z_category).  # Not Y!
        contains(m1, x_item). contains(m1, z_item).
    """)
    assert not result.holds("is_X_Y_mixture(m1)")

Step 5: Document in PR

Include in PR description:

## New Predicate: is_X_Y_mixture/1

**Purpose**: Detects mixtures containing both X and Y categories.

**Signature**: `is_X_Y_mixture(mixture)`

**Justification**: Needed for YD XX:Y rules that apply specifically
to X+Y combinations. No existing predicate captures this.

**Usage Example**:
```prolog
asserts(mechaber, issur(achiila, M, d_rabanan)) :-
    is_X_Y_mixture(M).

Tests Added: 2 positive, 2 negative cases

## Common Predicate Mistakes

### Mistake 1: Wrong Arity

```prolog
% WRONG - issur takes 3 arguments
issur(M).                      % Missing action and madrega
issur(achiila, M).             % Missing madrega

% CORRECT
issur(achiila, M, d_rabanan).

Mistake 2: Wrong Sort

% WRONG - food_type second arg must be food_category
food_type(chicken, animal).    % 'animal' not in food_category enum

% CORRECT - use valid food_category
food_type(chicken, of).        % 'of' is valid food_category

Mistake 3: Using String Instead of Atom

% WRONG - quoted string where atom expected
food_type("chicken", of).      % "chicken" is string

% CORRECT - unquoted atom
food_type(chicken, of).        % chicken is atom

Mistake 4: Creating Duplicate Predicate

% WRONG - creating synonym of food_type
food_category(chicken, of).    % This already exists as food_type!

% CORRECT - use existing predicate
food_type(chicken, of).

Mistake 5: Over-Engineering New Predicates

% WRONG - overly specific predicate for one rule
is_chicken_in_pot_with_milk_on_fire(M).

% CORRECT - composable predicates
is_of_chalav_mixture(M).
in_vessel(M, V).
on_heat_source(V).

Predicate Selection Checklist

Before using a predicate:

  • [ ] Checked predicate registry for existence
  • [ ] Verified arity matches usage
  • [ ] Confirmed sort compatibility for each argument
  • [ ] Used enum values from correct enumeration
  • [ ] Avoided negating OWA predicates
  • [ ] Used existing predicates before creating new ones

Before creating new predicate:

  • [ ] Verified no existing predicate serves purpose
  • [ ] Documented predicate specification
  • [ ] Added comprehensive tests
  • [ ] Included in PR documentation