Tutorial 4: Writing Rules¶
This tutorial teaches you how to write halachic rules using HLL (Halachic Logic Language), Mistaber's domain-specific language.
Learning Objectives¶
- Understand HLL syntax and directives
- Write rules with proper source citations (makor)
- Set normative levels (madrega)
- Compile HLL to ASP
- Test your rules
Prerequisites¶
- Tutorial 3: Understanding Worlds completed
- Basic understanding of logical rules
Estimated Time¶
25 minutes
Steps¶
Step 1: Introduction to HLL¶
HLL (Halachic Logic Language) is a domain-specific language that compiles to Answer Set Programming (ASP). It provides:
- Directives for metadata (
@world,@rule,@makor,@madrega) - Surface syntax for common patterns (
basar(X)->food_type(X, basar)) - Type checking to catch errors early
- Normative requirements enforcing source citations
Step 2: Basic Rule Structure¶
An HLL rule looks like this:
@world(mechaber)
@rule(r_example_rule)
@makor([sa("yd:87:1")])
@madrega(d_rabanan)
conclusion(X) :- condition1(X), condition2(X).
Let's break this down:
| Element | Purpose |
|---|---|
@world(mechaber) |
Rule applies in the mechaber world |
@rule(r_example_rule) |
Unique identifier for the rule |
@makor([sa("yd:87:1")]) |
Source: Shulchan Aruch YD 87:1 |
@madrega(d_rabanan) |
Normative level: rabbinic |
conclusion(X) :- ... |
The actual logical rule |
Step 3: Write Your First HLL Rule¶
Create a file called my_rules.hll:
% my_rules.hll
% A custom rule for demonstration
@world(base)
@rule(r_forbidden_unknown_source)
@makor([custom("demo:1")])
@madrega(d_rabanan)
% If a food has unknown source, it's forbidden to eat
issur(achiila, F) :- food(F), unknown_source(F).
This rule says: "According to base halacha, eating food with unknown source is rabbinically forbidden."
Step 4: Compile HLL to ASP¶
Use the compiler to convert HLL to ASP:
from mistaber.dsl.compiler.compiler import compile_hll
hll_source = """
@world(base)
@rule(r_my_rule)
@makor([sa("yd:87:1")])
@madrega(d_rabanan)
forbidden(F) :- basar(F), chalav(F).
"""
asp_code = compile_hll(hll_source)
print("Generated ASP:")
print(asp_code)
Output:
% Generated ASP:
rule(r_my_rule).
makor(r_my_rule, sa("yd:87:1")).
madrega(r_my_rule, d_rabanan).
scope(r_my_rule, base).
forbidden(F) :- food_type(F, basar), food_type(F, chalav).
Notice how:
- Metadata becomes facts (rule/1, makor/2, madrega/2, scope/2)
- basar(F) was normalized to food_type(F, basar)
Step 5: HLL Directives¶
@world - Scope Declaration¶
Specifies which world this rule applies to:
@world(mechaber) % Applies to mechaber world
@world(rema) % Applies to rema world
@world(base) % Applies to all (inherited)
@rule - Rule Identifier¶
Every normative rule needs a unique ID:
@rule(r_bb_beheima_achiila) % Descriptive naming
@rule(r_yd87_1) % Source-based naming
@rule(r_my_custom_rule) % Custom naming
Naming convention: r_[topic]_[detail]
@makor - Source Citation¶
Required for normative rules. Supports multiple sources:
@makor([sa("yd:87:1")]) % Shulchan Aruch
@makor([rema("yd:87:3")]) % Rema's gloss
@makor([sa("yd:87:1"), rema("yd:87:1")]) % Multiple sources
@makor([taz("yd:87:3"), shach("yd:87:5")]) % Commentators
@makor([gemara("pesachim:76b")]) % Talmudic source
@madrega - Normative Level¶
Sets the strength of the prohibition:
@madrega(d_oraita) % Torah-level (strongest)
@madrega(d_rabanan) % Rabbinic level
@madrega(minhag) % Custom
@madrega(chumra) % Stringency (weakest)
Step 6: Surface Syntax Shortcuts¶
HLL provides shortcuts for common patterns:
| HLL Shortcut | Expands To |
|---|---|
basar(X) |
food_type(X, basar) |
chalav(X) |
food_type(X, chalav) |
beheima(X) |
food_type(X, beheima) |
of(X) |
food_type(X, of) |
dag(X) |
food_type(X, dag) |
issur(Act, Item) |
issur(Act, Item, madrega) |
mutar(Act, Item) |
heter(Act, Item) |
Example:
% HLL (readable)
@world(base)
@rule(r_test)
@makor([sa("yd:87:1")])
@madrega(d_rabanan)
forbidden(F) :- of(F), chalav(F).
% Compiles to ASP:
forbidden(F) :- food_type(F, of), food_type(F, chalav).
Step 7: Writing Mixture Rules¶
Rules about mixtures use the mixture/1 and contains/2 predicates:
@world(mechaber)
@rule(r_beheima_chalav_mix)
@makor([sa("yd:87:1")])
@madrega(d_oraita)
% Eating a beheima+chalav mixture is forbidden d'oraita
asserts(mechaber, issur(achiila, M, d_oraita)) :-
mixture(M),
contains(M, Basar),
contains(M, Dairy),
food_type(Basar, beheima),
food_type(Dairy, chalav).
Step 8: Type Checking and Validation¶
The compiler checks for:
- Undeclared predicates: Unknown predicates raise errors
- Arity mismatches: Wrong number of arguments
- Missing makor: Normative rules need sources
- OWA negation warnings: Using
noton open-world predicates
from mistaber.dsl.compiler.compiler import compile_hll, CompileError
# This will raise an error - missing makor
try:
compile_hll("""
@world(base)
@rule(r_test)
forbidden(X) :- bad_thing(X).
""")
except CompileError as e:
print(f"Error: {e}")
# This will succeed with a warning
asp, warnings = compile_hll("""
@world(base)
@rule(r_test)
@makor([sa("yd:1:1")])
permitted(F) :- not is_forbidden(F).
""", return_warnings=True)
for w in warnings:
print(f"Warning: {w.message}")
Step 9: Complete Example - Encoding a New Rule¶
Let's encode a real halachic rule from scratch. We'll add a rule about "davar sheyesh lo matirin" (something that will become permitted):
% davar_sheyesh_lo_matirin.hll
% Rule: Items that will become permitted are not nullified
@world(base)
@rule(r_davar_sheyesh_lo_matirin)
@makor([sa("yd:102:1"), rema("yd:102:1")])
@madrega(d_rabanan)
% If an item will become permitted (e.g., before its time),
% it cannot be nullified even in a majority
min_lo_batel(M) :-
mixture(M),
contains(M, F),
will_become_permitted(F).
% The issur remains even with shishim (60:1)
issur(achiila, M) :-
mixture(M),
contains(M, F),
will_become_permitted(F),
issur_present(F).
Step 10: Test Your Rules¶
After writing rules, test them:
from pathlib import Path
from mistaber.engine import HsrsEngine
from mistaber.dsl.compiler.compiler import compile_hll
# Compile the HLL
hll_source = """
@world(base)
@rule(r_test_rule)
@makor([sa("yd:1:1")])
@madrega(d_rabanan)
asserts(base, custom_issur(F)) :- food(F), is_suspect(F).
"""
asp_code = compile_hll(hll_source)
# Create scenario to test
engine = HsrsEngine(Path("mistaber/ontology"))
test_scenario = f"""
{asp_code}
food(suspicious_item).
is_suspect(suspicious_item).
"""
result = engine.analyze(test_scenario, world="base")
# Check if our rule fired
custom_issurim = [a for a in result["atoms"] if "custom_issur" in a]
print(f"Custom issur derived: {custom_issurim}")
Best Practices¶
1. Always Cite Sources¶
Every normative rule must have a makor. This ensures traceability:
% Good
@rule(r_my_rule)
@makor([sa("yd:87:1")])
% Bad - will fail compilation
@rule(r_my_rule)
% no makor
2. Use Descriptive Rule Names¶
3. Set Appropriate Madrega¶
Match the normative level to the source:
% Torah-level prohibition
@madrega(d_oraita)
% Rabbinic decree
@madrega(d_rabanan)
% Community custom
@madrega(minhag)
4. Document Complex Logic¶
Use comments to explain the halachic reasoning:
% SA YD 87:3 - Poultry with milk
% The Gemara (Chullin 104b) derives that "gedi" excludes fowl,
% making the prohibition only d'rabanan
@rule(r_of_chalav)
@makor([sa("yd:87:3"), gemara("chullin:104b")])
@madrega(d_rabanan)
Exercises¶
Exercise 1: Write a Basic Rule¶
Write an HLL rule that forbids eating something that is both meat and fish together.
Solution
Exercise 2: Add World-Specific Override¶
Write rules for both Mechaber and Rema on a disputed topic.
Solution
% Mechaber: Stringent view
@world(mechaber)
@rule(r_strict_view)
@makor([sa("yd:1:1")])
@madrega(d_rabanan)
asserts(mechaber, issur(achiila, item_x)) :- condition(item_x).
% Rema: Override with lenient view
@world(rema)
@rule(r_lenient_view)
@makor([rema("yd:1:1")])
override(rema, issur(achiila, item_x), lenient_tradition).
asserts(rema, heter(achiila, item_x)) :- condition(item_x).
What You've Learned¶
- HLL syntax with directives (@world, @rule, @makor, @madrega)
- How to write rules using surface syntax shortcuts
- How to compile HLL to ASP
- Type checking and validation
- Best practices for rule authoring
Next Steps¶
Continue to Tutorial 5: Encoding Machloket to learn how to model halachic disputes.
Production Encoding Workflow
For systematic encoding of halacha with human review checkpoints, see the Encoding Workflow Guide which uses the mistaber-skills Claude Code plugin for AI-assisted encoding.