Rules

The Arg2p framework allows the encoding of both strict and defeasible rules.

Defeasible Rules

All statements are expressed in this form:

RuleName: Premise1, ..., PremiseN => Conclusion.

The absence of premises can be expressed with the notation:

RuleName: [] => Conclusion.

It is also possible to encode defeasible/ordinary premises with the notation:

FactName :=> Conclusion.

Try it — a rule and the premise that triggers it.

As for the form that premises and conclusions can take, all the properties of prolog terms (atoms, variables, lists, compound terms) are allowed.

Strict Rules

All statements are expressed in this form:

RuleName: Premise1, ..., PremiseN -> Conclusion.

The absence of premises can be expressed with the notation:

RuleName: [] -> Conclusion.

It is also possible to encode axioms premises with the notation:

FactName :-> Conclusion.

Axioms or ordinary premises?

The distinction matters: an axiom cannot be attacked. Rebutting and undermining only apply to arguments with a defeasible component, and an axiom has none, so a conflicting argument cannot touch it.

f0 :=> p.       % ordinary premise: p and -p are both undecided
a1 :-> p.       % axiom: p holds, -p is out
f1 :=> -p.

Use :-> for what the theory takes for granted — statutory text, agreed facts, definitions — and :=> for what a counter-argument should be able to challenge.

Try the axiom and compare it with the ordinary premise.

As for the form that premises and conclusions can take, all the properties of prolog terms (atoms, variables, lists, compound terms) are allowed.

Conflicts

The contrast between terms, at the base of rebuts and undermine attacks, can be reached through negation.

Strong negation

-Term

indicates a strong negation, as opposed to the negation as failure implemented within the tuProlog engine. Strong negation cannot be nested.

Try it — two premises supporting d and -d leave both undecided.

Weak negation

~(Term)

~(Term) indicates a weak negation – i.e., negation by failure – as the ability to encode rules exceptions. Weak negations are meant for the body of a rule (premises): in a conclusion, ~(Term) is just a statement. Accordingly, the rule:

r : ~(Term1), Term2 => Conclusions.

should be read as unless Term1, if Term2 then Conclusions.

Try it — knowing Tweety is a penguin defeats the assumption: flies(tweety) is OUT.

Undercut

Undercut attacks can be expressed through the notation:

undercut(ruleName)

where ruleName is the identifier of a defeasible rule in the theory. For example, we could write:

r0 : something => conclusion.
r1 : some_other_thing => undercut(r0).

Negating the rule name has the same effect, since a rule name conflicts with its own negation:

r1 : some_other_thing => -r0.

Both forms make r0 inapplicable; pick whichever reads better. The explicit undercut/1 says what is meant, while -r0 composes with everything else that works on conclusions — it can itself be attacked, preferred, or derived by a chain of rules.

Try the explicit form and the negated rule name — both leave conclusion out.

Permission and obligation

p(Term)
o(Term)

to indicate permission and obligation respectively. These concepts, belonging to the deontic expansion of classical logic, allow obtaining the flexibility necessary to deal with prohibitions and rights. For instance:

v_rule: o(-enter), enter => violation.

Try it

Currently, admitted forms for permission and obligation are:

o(Term)     % obligation
o(-Term)    % prohibition
-o(Term)    % no obligation
-o(-Term)   % no prohibition
p(Term)     % permission to do something
p(-Term)    % permission to don't do something
-p(Term)    % prohibition
-p(-Term)   % obligation (not permitted not to)

where Term is a standard Prolog term.

Superiority Relation

It is possible to express these preferences with the following notation:

sup(RuleNam1, RuleName2).

This proposition symbolises the greater reliability of the rule with identifier equal to RuleName 1 over that with identifier RuleName 2.

Try it — without sup/2 both conclusions are undecided; with it, q wins.

The preferences are transitive: sup(r1, r2) and sup(r2, r3) also prefer r1 to r3, with no need to state it. A cycle (sup(r1, r2) and sup(r2, r1)) never makes a rule preferred to itself. The preferences concluded by rules (defeasible preferences) are closed by a rule of the theory instead.

Custom conflicts

Beyond the built-in conflicts — a statement against its strong negation, and the deontic pairs — a theory can declare its own:

conflict([a], [b]).                 % a and b are incompatible
conflict([a], [b], Guard).          % ... whenever the Prolog goal Guard succeeds

Both arguments are lists, mirroring the way conclusions are stored. With the fact above, an argument for a attacks one for b: the conflict goes one way, so state conflict([b], [a]) too for them to attack each other, as a and -a do.

conflict([a], [b]).
f1 :=> a.
f2 :=> b.

Try it — querying b shows the conflict at work; remove the conflict/2 fact and it is accepted again.

To let rules derive conflicts instead of declaring them, see Meta-argumentation.

Calling Prolog from a rule

A premise wrapped in prolog/1 is executed as an ordinary Prolog goal instead of being treated as a statement to be argued for:

r1 : prolog(2 < 3) => ok.

The rule applies only if the goal succeeds, and the goal itself is never attacked — it is a computation, not a claim. Try it

This is the way to use arithmetic, comparisons, or any other Prolog builtin inside a theory:

r_adult : age(X), prolog(X >= 18) => adult.
f1 :=> age(20).

If the goal fails, no argument is built from that rule at all.

A prolog/1 goal is solved with the bindings of the premises written before it — like X above, from the age/1 premise — both on the whole graph and under queryMode: write it after the premises binding its variables (prolog(X >= 18), age(X) raises an instantiation error).

Theories in plain Prolog

With the prologStrictCompatibility flag, the Prolog clauses of a theory are argued as well: a fact is an axiom and a clause a strict rule. A comment of a single word after the full stop of a clause, on the same line, names it and makes it defeasible: a fact so named is a premise, a clause a defeasible rule.

prologStrictCompatibility.

bird(tweety).
flies(X) :- bird(X).
-flies(X) :-
    penguin(X).     % r1
penguin(tweety).    % f1

Here bird(tweety) is an axiom and flies(X) :- bird(X) a strict rule, while r1 is a defeasible rule and f1 a premise. Any other comment, of more words or on a line of its own, leaves its clause as it is. The identifiers are read from the text of the theory, by every entry point of Arg2P (the factories in Kotlin and JavaScript, modules loaded with call_module/2); a theory built as terms has none.

Burden of proof

The burden of persuasion indication can be expressed as:

bp(Term1,…, TermN).

Declared this way it is read by the bp_grounded family of semantics. Try it — the link carries the bp_grounded flag block.

A burden that should itself be open to argument is written as a rule conclusion instead — see Meta-argumentation.