Tutorial: Handling Machloket¶
This tutorial teaches you to encode halachic disputes (machloket) between authorities using Mistaber's multi-world architecture and override mechanism. You will encode the famous dispute between the Mechaber and Rema regarding fish and dairy (YD 87:3).
Duration: 2 hours Difficulty: Intermediate Prerequisites: Tutorial 01: Your First Encoding Target: YD 87:3 (Fish and Dairy Dispute)
What You Will Encode¶
The dispute regarding fish cooked with dairy is one of the most interesting machloket in Yoreh Deah:
- Mechaber (Sefardi): Fish with dairy is forbidden due to health danger (sakana)
- Rema (Ashkenazi): Fish with dairy is permitted (no sakana concern)
This dispute is perfect for learning machloket encoding because:
- Clear disagreement: Two explicit opposing positions
- Different reasoning: Sakana vs. no halachic basis for concern
- World inheritance: Child worlds inherit their parent's position
- Override mechanics: Rema must override Mechaber's position
Understanding the Dispute¶
The Mechaber's Position¶
The Mechaber (R' Yosef Karo), based on medical authorities cited in the Beit Yosef, holds that cooking fish with milk poses a health danger (sakana):
"Fish with dairy is dangerous to one's health and should be avoided."
Key Points:
- This is sakana (health danger), not issur (halachic prohibition)
- Sakana is treated more stringently than regular issur in some respects
- Based on medical understanding of his era (16th century)
Sources:
- Shulchan Aruch YD 87:3 (by implication)
- Beit Yosef on Tur YD 87 (citing medical texts)
The Rema's Position¶
The Rema (R' Moshe Isserles) does not gloss on the Mechaber's statement about fish and dairy. In halachic literature, significant silence often indicates disagreement. The Ashkenazi tradition permits fish with dairy.
Key Points:
- No Talmudic source for fish + dairy sakana
- Ashkenazi practice is to permit (lox and cream cheese, etc.)
- Later poskim (Taz, Shach) provide explicit justification
Sources:
- Rema's silence on SA YD 87:3
- Taz YD 87:3 (scribal error argument)
- Shach YD 87:5 (conditional interpretation)
The Taz's Revolutionary Interpretation¶
The Taz argues the entire sakana ruling is based on a scribal error:
- The Gemara (Pesachim 76b) discusses fish with meat being dangerous
- A copyist mistakenly wrote "milk" (chalav) instead of "meat" (basar)
- This error propagated through subsequent texts
- There is no authentic source for fish + dairy being dangerous
The Shach's Conditional Position¶
The Shach takes a middle ground: even according to the Mechaber, the sakana only applies to fish cooked together with dairy, not cold mixtures.
The Override Mechanism¶
Before encoding, understand how Mistaber handles disputes:
World Inheritance¶
graph TD
subgraph "Without Override"
M1[mechaber asserts sakana] --> S1[sefardi_yo inherits sakana]
end
subgraph "With Override"
M2[mechaber asserts sakana] -.->|blocked| R2[rema: override → no_sakana]
R2 --> MB2[ashk_mb inherits no_sakana]
R2 --> AH2[ashk_ah inherits no_sakana]
end
The override/3 Predicate¶
Syntax: override(World, OverriddenProp, DescriptiveValue)
The third argument must be a descriptive value indicating the new position:
% CORRECT - descriptive value
override(rema, sakana(M), no_sakana) :-
is_dag_chalav_mixture(M).
% WRONG - never use 'invalid'
override(rema, sakana(M), invalid) :- % DO NOT DO THIS
is_dag_chalav_mixture(M).
Critical: Never Use 'invalid' in Override/3
The third argument describes what the child world holds instead. Use meaningful terms:
no_sakana(not invalid)permitted(not false)lenient_ruling(not null)
Step-by-Step Encoding¶
Step 1: Start the Session¶
The corpus-prep skill will identify this as a machloket and provide both positions in the report.
Step 2: Review the Corpus Report¶
Corpus Report: Fish & Dairy Machloket
# Corpus Report: YD 87:3 (Fish & Dairy)
## MACHLOKET IDENTIFIED
This seif contains a **documented machloket** between:
- Mechaber (Sefardi): Forbidden due to sakana
- Rema (Ashkenazi): Permitted
## Primary Source: Mechaber
**SA YD 87:3** (relevant excerpt):
> "Dagim v'chagavim ein bahem issur afilu miderabanan"
> (Fish and locusts have no [basar bechalav] prohibition even rabbinically)
**Beit Yosef on Tur YD 87**:
> Fish with milk is dangerous and should be avoided due to sakana.
## Rema's Position
**No explicit gloss** on the dairy aspect of this seif.
Later Ashkenazi poskim clarify this silence indicates permission.
## Commentary Classification
| Commentary | Position | Notes |
|------------|----------|-------|
| Taz | Permits | Scribal error argument |
| Shach | Conditional | Only if cooked together |
| Pri Megadim | Follows Rema | Standard Ashkenazi |
## Derivation Chain: Mechaber
```mermaid
graph TD
Medical["Medical texts<br/>(cited by Beit Yosef)"] --> BY
BY["Beit Yosef YD 87"] --> SA
SA["SA YD 87:3 (implied)"]
Derivation Chain: Rema¶
graph TD
NoSource["No Talmudic source<br/>for fish+dairy sakana"] --> Silence
Silence["Rema's silence"] --> Practice
Practice["Ashkenazi practice permits"]
Taz["Taz: Scribal error"] --> Practice
Shach["Shach: Conditional only"] --> Practice
Complexity Score: 7/10¶
- Explicit machloket requiring both encodings
- Override mechanism required
- Child world inheritance verification needed
- Multiple source layers
Questions for Human Review¶
- Should Mechaber's position be encoded as sakana (health) or issur (prohibition)?
-
[Default: sakana - as this is the Mechaber's basis]
-
Should the Taz's scribal error interpretation be encoded as an interpretation layer, or is Rema's override sufficient?
- [Default: Rema's override sufficient for core encoding; Taz as interpretation layer for future enrichment] ```
Step 3: Approve Corpus with Answers¶
User: "Approved. Answers:
1. Encode as sakana (health concern), which then implies issur achiila.
2. Rema's override is sufficient. Taz can be added later as interpretation."
Step 4: Encode the Mechaber's Position¶
After corpus approval, run encoding:
The first encoded position is the Mechaber's:
% ============================================================================
% YD 87:3 - DAG (FISH) + CHALAV: SAKANA (MECHABER'S POSITION)
% ============================================================================
% The Mechaber considers fish with milk to be forbidden due to sakana
% (health danger), based on medical authorities cited in Beit Yosef.
%
% While SA YD 87:3 states fish has no basar bechalav issur, the Mechaber's
% position from Beit Yosef treats fish+milk as forbidden due to sakana.
%
% DISPUTANTS:
% - Mechaber: sakana (forbidden)
% - Rema: permitted (overrides)
%
% AFFECTED WORLDS:
% - mechaber → sefardi_yo: inherit sakana
% - rema → ashk_mb, ashk_ah: inherit permission
% === Mechaber's Position ===
rule(r_bb_dag_sakana).
makor(r_bb_dag_sakana, sa("yd:87:3")).
makor(r_bb_dag_sakana, beit_yosef("yd:87")).
scope(r_bb_dag_sakana, mechaber).
% Assert sakana (health danger) for fish + dairy
asserts(mechaber, sakana(M)) :-
is_dag_chalav_mixture(M).
% Derive issur from sakana
% The action is forbidden because it poses health danger
asserts(mechaber, issur(achiila, M, sakana)) :-
is_dag_chalav_mixture(M).
Key Points:
- Rule ID is semantic:
r_bb_dag_sakana - Multiple makor citations (SA and Beit Yosef)
- Scope is explicitly
mechaber - Two assertions:
sakana(M)andissur(achiila, M, sakana)
Step 5: Encode the Rema's Override¶
The Rema's position requires the override mechanism:
% ============================================================================
% YD 87:3 - DAG (FISH) + CHALAV: PERMITTED (REMA'S OVERRIDE)
% ============================================================================
% The Rema and Ashkenazi poskim permit fish with dairy.
% Rema's silence indicates disagreement with Mechaber's sakana ruling.
%
% Later support:
% - Taz: The sakana ruling is a scribal error
% - Shach: At most, only applies if cooked together
%
% This OVERRIDES the Mechaber's sakana ruling in Ashkenazi worlds.
% === Rema's Override Position ===
rule(r_rema_dag_chalav_mutar).
makor(r_rema_dag_chalav_mutar, rema("yd:87:3")).
makor(r_rema_dag_chalav_mutar, taz("yd:87:3")).
makor(r_rema_dag_chalav_mutar, shach("yd:87:5")).
scope(r_rema_dag_chalav_mutar, rema).
% === OVERRIDE MECHANISM ===
% Block inheritance of sakana from mechaber
% Third argument is DESCRIPTIVE VALUE (never 'invalid')
override(rema, sakana(M), no_sakana) :-
is_dag_chalav_mixture(M).
% Block inheritance of issur based on sakana
override(rema, issur(achiila, M, sakana), permitted) :-
is_dag_chalav_mixture(M).
% === POSITIVE ASSERTIONS ===
% Explicitly assert the permissive position
% Eating fish with dairy is permitted
asserts(rema, heter(achiila, M)) :-
is_dag_chalav_mixture(M).
% Cooking fish with dairy is permitted
asserts(rema, heter(bishul, M)) :-
is_dag_chalav_mixture(M).
% Benefit from fish + dairy is permitted
asserts(rema, heter(hanaah, M)) :-
is_dag_chalav_mixture(M).
% Explicitly assert no sakana
asserts(rema, no_sakana(M)) :-
is_dag_chalav_mixture(M).
% === MACHLOKET MARKER ===
% Document this dispute for discoverability
machloket(dag_chalav_sakana, mechaber, rema, M) :-
is_dag_chalav_mixture(M).
Key Points:
- Override with descriptive value:
no_sakana, notinvalid - Both overrides needed: Block both
sakanaandissur(...sakana) - Positive assertions: Don't just block; assert what IS true
- Machloket marker: Use
machloket/4for discoverability
Step 6: Verify World Inheritance¶
The encoding must correctly propagate through child worlds:
graph TD
subgraph "Mechaber Branch"
M[mechaber<br/>sakana, issur] --> SY[sefardi_yo<br/>inherits sakana, issur]
end
subgraph "Rema Branch"
R[rema<br/>override: no_sakana, heter] --> MB[ashk_mb<br/>inherits no_sakana, heter]
R --> AH[ashk_ah<br/>inherits no_sakana, heter]
end
style M fill:#ffcdd2
style SY fill:#ffcdd2
style R fill:#c8e6c9
style MB fill:#c8e6c9
style AH fill:#c8e6c9
| World | Inherits From | Fish + Dairy Status |
|---|---|---|
mechaber |
base | sakana (forbidden) |
sefardi_yo |
mechaber | Inherits sakana |
rema |
base (with override) | no_sakana (permitted) |
ashk_mb |
rema | Inherits heter |
ashk_ah |
rema + gra | Inherits heter |
gra |
base | Likely follows Rema (verify) |
Step 7: Write Multi-World Tests¶
Testing machloket requires verifying both positions and their inheritance:
"""Tests for fish+dairy machloket between Mechaber and Rema."""
import pytest
from mistaber import query
class TestFishDairyMachloket:
"""Comprehensive tests for the fish+dairy dispute."""
@pytest.fixture
def fish_dairy_mixture(self):
"""Standard fish + dairy mixture for testing."""
return """
mixture(m1).
food(salmon). food_type(salmon, dag).
food(cream). food_type(cream, chalav).
contains(m1, salmon). contains(m1, cream).
"""
# =========================================================
# MECHABER POSITION TESTS
# =========================================================
def test_mechaber_asserts_sakana(self, fish_dairy_mixture):
"""Mechaber holds fish+dairy is sakana (health danger)."""
result = query(fish_dairy_mixture, world="mechaber")
assert result.holds("sakana(m1)")
def test_mechaber_forbids_eating(self, fish_dairy_mixture):
"""Mechaber forbids eating fish+dairy due to sakana."""
result = query(fish_dairy_mixture, world="mechaber")
assert result.holds("issur(achiila, m1, sakana)")
def test_mechaber_no_heter(self, fish_dairy_mixture):
"""Mechaber does NOT assert heter for fish+dairy."""
result = query(fish_dairy_mixture, world="mechaber")
assert not result.holds("heter(achiila, m1)")
def test_sefardi_yo_inherits_sakana(self, fish_dairy_mixture):
"""Sefardi YO (child of mechaber) inherits sakana."""
result = query(fish_dairy_mixture, world="sefardi_yo")
assert result.holds("sakana(m1)")
assert result.holds("issur(achiila, m1, sakana)")
def test_sefardi_yo_no_heter(self, fish_dairy_mixture):
"""Sefardi YO does NOT permit fish+dairy."""
result = query(fish_dairy_mixture, world="sefardi_yo")
assert not result.holds("heter(achiila, m1)")
# =========================================================
# REMA POSITION TESTS
# =========================================================
def test_rema_no_sakana(self, fish_dairy_mixture):
"""Rema holds no sakana for fish+dairy."""
result = query(fish_dairy_mixture, world="rema")
assert result.holds("no_sakana(m1)")
assert not result.holds("sakana(m1)")
def test_rema_permits_eating(self, fish_dairy_mixture):
"""Rema permits eating fish+dairy."""
result = query(fish_dairy_mixture, world="rema")
assert result.holds("heter(achiila, m1)")
def test_rema_permits_cooking(self, fish_dairy_mixture):
"""Rema permits cooking fish+dairy."""
result = query(fish_dairy_mixture, world="rema")
assert result.holds("heter(bishul, m1)")
def test_rema_permits_benefit(self, fish_dairy_mixture):
"""Rema permits benefit from fish+dairy."""
result = query(fish_dairy_mixture, world="rema")
assert result.holds("heter(hanaah, m1)")
def test_rema_no_issur(self, fish_dairy_mixture):
"""Rema does NOT assert issur for fish+dairy."""
result = query(fish_dairy_mixture, world="rema")
assert not result.holds("issur(achiila, m1, sakana)")
# =========================================================
# CHILD WORLD INHERITANCE TESTS
# =========================================================
def test_ashk_mb_inherits_heter(self, fish_dairy_mixture):
"""Mishnah Berurah (child of rema) inherits permission."""
result = query(fish_dairy_mixture, world="ashk_mb")
assert result.holds("heter(achiila, m1)")
assert result.holds("no_sakana(m1)")
def test_ashk_mb_no_sakana(self, fish_dairy_mixture):
"""Mishnah Berurah does NOT inherit sakana."""
result = query(fish_dairy_mixture, world="ashk_mb")
assert not result.holds("sakana(m1)")
def test_ashk_ah_inherits_heter(self, fish_dairy_mixture):
"""Aruch HaShulchan (child of rema+gra) inherits permission."""
result = query(fish_dairy_mixture, world="ashk_ah")
assert result.holds("heter(achiila, m1)")
def test_ashk_ah_no_sakana(self, fish_dairy_mixture):
"""Aruch HaShulchan does NOT inherit sakana."""
result = query(fish_dairy_mixture, world="ashk_ah")
assert not result.holds("sakana(m1)")
# =========================================================
# MACHLOKET DETECTION TESTS
# =========================================================
def test_machloket_marked(self, fish_dairy_mixture):
"""The machloket is properly documented."""
result = query(fish_dairy_mixture, world="base")
assert result.holds("machloket(dag_chalav_sakana, mechaber, rema, m1)")
def test_cross_world_divergence(self, fish_dairy_mixture):
"""Mechaber and Rema diverge on this issue."""
mechaber_result = query(fish_dairy_mixture, world="mechaber")
rema_result = query(fish_dairy_mixture, world="rema")
# Opposite positions
assert mechaber_result.holds("sakana(m1)")
assert not rema_result.holds("sakana(m1)")
assert mechaber_result.holds("issur(achiila, m1, sakana)")
assert rema_result.holds("heter(achiila, m1)")
# =========================================================
# EDGE CASES
# =========================================================
def test_only_fish_no_mixture(self, fish_dairy_mixture):
"""Fish alone (no dairy) has no sakana."""
fish_only = """
food(salmon). food_type(salmon, dag).
"""
result = query(fish_only, world="mechaber")
assert not result.holds("sakana(_)")
def test_only_dairy_no_mixture(self, fish_dairy_mixture):
"""Dairy alone (no fish) has no sakana."""
dairy_only = """
food(cream). food_type(cream, chalav).
"""
result = query(dairy_only, world="mechaber")
assert not result.holds("sakana(_)")
@pytest.mark.parametrize("world,expect_sakana", [
("mechaber", True),
("sefardi_yo", True),
("rema", False),
("ashk_mb", False),
("ashk_ah", False),
])
def test_fish_dairy_sakana_by_world(fish_dairy_mixture, world, expect_sakana):
"""Parameterized test: sakana status varies by world."""
result = query(fish_dairy_mixture, world=world)
if expect_sakana:
assert result.holds("sakana(m1)"), f"{world} should have sakana"
else:
has_no_sakana = (result.holds("no_sakana(m1)") or
not result.holds("sakana(m1)"))
assert has_no_sakana, f"{world} should NOT have sakana"
Step 8: Run Validation¶
Expected output:
=== Compilation ===
SUCCESS: All files compile without errors
=== Semantic Checks ===
[PASS] Override values are descriptive
[PASS] Both positions have makor citations
[PASS] Machloket is marked with machloket/4
=== Test Results ===
20 tests collected
20 passed
=== Multi-World Verification ===
[PASS] mechaber: sakana asserted
[PASS] sefardi_yo: sakana inherited
[PASS] rema: sakana overridden
[PASS] ashk_mb: heter inherited
[PASS] ashk_ah: heter inherited
NO REGRESSIONS DETECTED
Step 9: Complete Review and Commit¶
Complete both checklists, then:
Common Machloket Encoding Mistakes¶
Mistake 1: Forgetting Positive Assertions¶
% WRONG - only overrides, no positive assertion
override(rema, sakana(M), no_sakana) :-
is_dag_chalav_mixture(M).
% What does Rema actually hold? Unclear!
% CORRECT - override PLUS positive assertion
override(rema, sakana(M), no_sakana) :-
is_dag_chalav_mixture(M).
asserts(rema, heter(achiila, M)) :- % Explicitly assert permission
is_dag_chalav_mixture(M).
asserts(rema, no_sakana(M)) :- % Explicitly assert no sakana
is_dag_chalav_mixture(M).
Mistake 2: Using 'invalid' in Override¶
% WRONG - invalid is not a descriptive value
override(rema, sakana(M), invalid) :-
is_dag_chalav_mixture(M).
% CORRECT - use meaningful descriptive value
override(rema, sakana(M), no_sakana) :-
is_dag_chalav_mixture(M).
Mistake 3: Missing Override for Derived Propositions¶
% PARTIALLY WRONG - overrides sakana but not the derived issur
override(rema, sakana(M), no_sakana) :-
is_dag_chalav_mixture(M).
% The issur(achiila, M, sakana) might still be inherited!
% CORRECT - override both the source and derived proposition
override(rema, sakana(M), no_sakana) :-
is_dag_chalav_mixture(M).
override(rema, issur(achiila, M, sakana), permitted) :-
is_dag_chalav_mixture(M).
Mistake 4: Missing machloket/4 Marker¶
% INCOMPLETE - machloket not documented
asserts(mechaber, sakana(M)) :- ...
override(rema, sakana(M), no_sakana) :- ...
% How do we discover this machloket programmatically?
% CORRECT - include machloket marker
machloket(dag_chalav_sakana, mechaber, rema, M) :-
is_dag_chalav_mixture(M).
Mistake 5: Not Testing Both Branches¶
# WRONG - only tests one position
def test_mechaber_sakana():
result = query(scenario, world="mechaber")
assert result.holds("sakana(m1)")
# What about Rema? What about child worlds?
# CORRECT - test both positions and inheritance
def test_mechaber_sakana(): ...
def test_rema_no_sakana(): ...
def test_sefardi_yo_inherits_sakana(): ...
def test_ashk_mb_inherits_heter(): ...
Summary: Machloket Encoding Checklist¶
Before completing a machloket encoding:
- [ ] Both positions encoded with full metadata
- [ ] Override uses descriptive value (never 'invalid')
- [ ] Positive assertions accompany all overrides
- [ ] All derived propositions also overridden if needed
- [ ] machloket/4 marker documents the dispute
- [ ] Tests verify both positions
- [ ] Tests verify child world inheritance
- [ ] Tests verify cross-world divergence
- [ ] No regressions in existing tests
The Complete Picture¶
After encoding this machloket, your world structure includes:
base
├── mechaber
│ └── sefardi_yo (inherits sakana)
│
├── rema (overrides: no_sakana)
│ ├── ashk_mb (inherits heter)
│ └── ashk_ah (inherits heter)
│
└── gra
└── ashk_ah (also inherits from gra)
Each world now produces correct fish+dairy rulings based on its tradition.
Next Steps¶
Now that you understand machloket encoding, continue with:
- Tutorial 03: Complex Conditions - Context-sensitive rules
- Tutorial 04: Testing Strategies - Comprehensive testing
- Tutorial 05: Review Process - Navigate reviews
Quick Reference: Machloket Patterns¶
% === Pattern 1: Override with positive assertion ===
override(child_world, parent_assertion(X), descriptive_value) :-
condition(X).
asserts(child_world, alternative_assertion(X)) :-
condition(X).
% === Pattern 2: Machloket marker ===
machloket(topic_name, authority1, authority2, Subject) :-
condition(Subject).
% === Pattern 3: Multi-world test ===
def test_machloket():
mechaber = query(scenario, world="mechaber")
rema = query(scenario, world="rema")
assert mechaber.holds("position_a(x)")
assert rema.holds("position_b(x)")