Skip to main content

argumentation_schemes/catalog/
practical.rs

1//! Practical schemes: reasoning about actions, consequences, and values.
2//!
3//! Ref: Walton, Reed & Macagno 2008, Chapter 9 + Appendix 1.
4
5use crate::catalog::PRACTICAL_ID_OFFSET;
6use crate::critical::CriticalQuestion;
7use crate::scheme::*;
8use crate::types::*;
9
10/// Return all practical schemes.
11pub fn all() -> Vec<SchemeSpec> {
12    vec![
13        argument_from_positive_consequences(),
14        argument_from_negative_consequences(),
15        argument_from_values(),
16        argument_from_threat(),
17        argument_from_fear_appeal(),
18        argument_from_waste(),
19        argument_from_sunk_cost(),
20    ]
21}
22
23/// Argument from Positive Consequences (Walton 2008 p.332).
24///
25/// If action A is brought about, good consequence G will occur.
26/// Therefore A should be brought about.
27pub fn argument_from_positive_consequences() -> SchemeSpec {
28    SchemeSpec {
29        id: SchemeId(PRACTICAL_ID_OFFSET),
30        name: "Argument from Positive Consequences".into(),
31        category: SchemeCategory::Practical,
32        premises: vec![
33            PremiseSlot::new("action", "The action being proposed", SlotRole::Action),
34            PremiseSlot::new(
35                "good_consequence",
36                "The beneficial outcome",
37                SlotRole::Consequence,
38            ),
39        ],
40        conclusion: ConclusionTemplate::positive(
41            "?action should be brought about because ?good_consequence will result",
42            "do_?action",
43        ),
44        critical_questions: vec![
45            CriticalQuestion::new(
46                1,
47                "Will ?action actually lead to ?good_consequence?",
48                Challenge::RuleValidity,
49            ),
50            CriticalQuestion::new(
51                2,
52                "Is ?good_consequence actually good on balance?",
53                Challenge::PremiseTruth("good_consequence".into()),
54            ),
55            CriticalQuestion::new(
56                3,
57                "Are there negative consequences that offset ?good_consequence?",
58                Challenge::UnseenConsequences,
59            ),
60        ],
61        metadata: SchemeMetadata {
62            citation: "Walton 2008 p.332".into(),
63            domain_tags: vec!["practical".into(), "consequences".into()],
64            presumptive: true,
65            strength: SchemeStrength::Moderate,
66        },
67    }
68}
69
70/// Argument from Negative Consequences (Walton 2008 p.333).
71///
72/// Negated-conclusion scheme: concludes ¬do_?action.
73pub fn argument_from_negative_consequences() -> SchemeSpec {
74    SchemeSpec {
75        id: SchemeId(PRACTICAL_ID_OFFSET + 1),
76        name: "Argument from Negative Consequences".into(),
77        category: SchemeCategory::Practical,
78        premises: vec![
79            PremiseSlot::new(
80                "action",
81                "The action being warned against",
82                SlotRole::Action,
83            ),
84            PremiseSlot::new(
85                "bad_consequence",
86                "The harmful outcome",
87                SlotRole::Consequence,
88            ),
89        ],
90        conclusion: ConclusionTemplate::negated(
91            "?action should not be brought about because ?bad_consequence will result",
92            "do_?action",
93        ),
94        critical_questions: vec![
95            CriticalQuestion::new(
96                1,
97                "Will ?action actually lead to ?bad_consequence?",
98                Challenge::RuleValidity,
99            ),
100            CriticalQuestion::new(
101                2,
102                "Is ?bad_consequence actually bad on balance?",
103                Challenge::PremiseTruth("bad_consequence".into()),
104            ),
105            CriticalQuestion::new(
106                3,
107                "Are there positive consequences that offset ?bad_consequence?",
108                Challenge::UnseenConsequences,
109            ),
110        ],
111        metadata: SchemeMetadata {
112            citation: "Walton 2008 p.333".into(),
113            domain_tags: vec!["practical".into(), "consequences".into()],
114            presumptive: true,
115            strength: SchemeStrength::Moderate,
116        },
117    }
118}
119
120/// Argument from Values (Walton 2008 p.321).
121pub fn argument_from_values() -> SchemeSpec {
122    SchemeSpec {
123        id: SchemeId(PRACTICAL_ID_OFFSET + 2),
124        name: "Argument from Values".into(),
125        category: SchemeCategory::Practical,
126        premises: vec![
127            PremiseSlot::new("action", "The action being evaluated", SlotRole::Action),
128            PremiseSlot::new("value", "The value being promoted", SlotRole::Property),
129            PremiseSlot::new(
130                "agent",
131                "The agent whose values are at stake",
132                SlotRole::Agent,
133            ),
134        ],
135        conclusion: ConclusionTemplate::positive(
136            "?action should be carried out because it promotes ?value for ?agent",
137            "do_?action",
138        ),
139        critical_questions: vec![
140            CriticalQuestion::new(
141                1,
142                "Does ?action really promote ?value?",
143                Challenge::RuleValidity,
144            ),
145            CriticalQuestion::new(
146                2,
147                "Is ?value relevant to the current context?",
148                Challenge::PremiseTruth("value".into()),
149            ),
150            CriticalQuestion::new(
151                3,
152                "Are there competing values that take precedence over ?value?",
153                Challenge::UnseenConsequences,
154            ),
155        ],
156        metadata: SchemeMetadata {
157            citation: "Walton 2008 p.321".into(),
158            domain_tags: vec!["practical".into(), "values".into()],
159            presumptive: true,
160            strength: SchemeStrength::Moderate,
161        },
162    }
163}
164
165/// Argument from Threat (Walton 2008 p.335).
166///
167/// Note: slot names `threatener` and `threat` overlap as prefixes —
168/// the `resolve_template` function in `instance.rs` sorts bindings by
169/// length descending to handle this correctly.
170pub fn argument_from_threat() -> SchemeSpec {
171    SchemeSpec {
172        id: SchemeId(PRACTICAL_ID_OFFSET + 3),
173        name: "Argument from Threat".into(),
174        category: SchemeCategory::Practical,
175        premises: vec![
176            PremiseSlot::new(
177                "threatener",
178                "The person making the threat",
179                SlotRole::Agent,
180            ),
181            PremiseSlot::new(
182                "threat",
183                "The bad thing that will happen",
184                SlotRole::Consequence,
185            ),
186            PremiseSlot::new("demand", "The action being demanded", SlotRole::Action),
187        ],
188        conclusion: ConclusionTemplate::positive(
189            "?demand should be complied with to avoid ?threat",
190            "comply_?demand",
191        ),
192        critical_questions: vec![
193            CriticalQuestion::new(
194                1,
195                "Does ?threatener have the ability to carry out ?threat?",
196                Challenge::PremiseTruth("threatener".into()),
197            ),
198            CriticalQuestion::new(
199                2,
200                "Is ?threat proportionate to ?demand?",
201                Challenge::Proportionality,
202            ),
203            CriticalQuestion::new(
204                3,
205                "Is there an alternative to complying with ?demand?",
206                Challenge::AlternativeCause,
207            ),
208        ],
209        metadata: SchemeMetadata {
210            citation: "Walton 2008 p.335".into(),
211            domain_tags: vec!["practical".into(), "coercion".into()],
212            presumptive: true,
213            strength: SchemeStrength::Weak,
214        },
215    }
216}
217
218/// Argument from Fear Appeal (Walton 2008 p.336).
219pub fn argument_from_fear_appeal() -> SchemeSpec {
220    SchemeSpec {
221        id: SchemeId(PRACTICAL_ID_OFFSET + 4),
222        name: "Argument from Fear Appeal".into(),
223        category: SchemeCategory::Practical,
224        premises: vec![
225            PremiseSlot::new("action", "The recommended action", SlotRole::Action),
226            PremiseSlot::new(
227                "fearful_outcome",
228                "The feared consequence of inaction",
229                SlotRole::Consequence,
230            ),
231        ],
232        conclusion: ConclusionTemplate::positive(
233            "?action should be taken to avoid ?fearful_outcome",
234            "do_?action",
235        ),
236        critical_questions: vec![
237            CriticalQuestion::new(
238                1,
239                "Is ?fearful_outcome truly likely if ?action is not taken?",
240                Challenge::RuleValidity,
241            ),
242            CriticalQuestion::new(
243                2,
244                "Is the fear of ?fearful_outcome proportionate to the actual risk?",
245                Challenge::Proportionality,
246            ),
247            CriticalQuestion::new(
248                3,
249                "Is ?action the only way to avoid ?fearful_outcome?",
250                Challenge::AlternativeCause,
251            ),
252        ],
253        metadata: SchemeMetadata {
254            citation: "Walton 2008 p.336".into(),
255            domain_tags: vec!["practical".into(), "emotion".into()],
256            presumptive: true,
257            strength: SchemeStrength::Weak,
258        },
259    }
260}
261
262/// Argument from Waste (Walton 2008 p.339).
263pub fn argument_from_waste() -> SchemeSpec {
264    SchemeSpec {
265        id: SchemeId(PRACTICAL_ID_OFFSET + 5),
266        name: "Argument from Waste".into(),
267        category: SchemeCategory::Practical,
268        premises: vec![
269            PremiseSlot::new("action", "The action already started", SlotRole::Action),
270            PremiseSlot::new(
271                "investment",
272                "What has been invested so far",
273                SlotRole::Property,
274            ),
275        ],
276        conclusion: ConclusionTemplate::positive(
277            "?action should be continued to avoid wasting ?investment",
278            "continue_?action",
279        ),
280        critical_questions: vec![
281            CriticalQuestion::new(
282                1,
283                "How much has actually been invested in ?action?",
284                Challenge::PremiseTruth("investment".into()),
285            ),
286            CriticalQuestion::new(
287                2,
288                "Would continuing ?action actually recoup ?investment?",
289                Challenge::RuleValidity,
290            ),
291        ],
292        metadata: SchemeMetadata {
293            citation: "Walton 2008 p.339".into(),
294            domain_tags: vec!["practical".into(), "sunk_cost".into()],
295            presumptive: true,
296            strength: SchemeStrength::Weak,
297        },
298    }
299}
300
301/// Argument from Sunk Cost (Walton 2008 p.340) — distinct from Waste:
302/// emphasises the prior commitment as a reason for continuation.
303pub fn argument_from_sunk_cost() -> SchemeSpec {
304    SchemeSpec {
305        id: SchemeId(PRACTICAL_ID_OFFSET + 6),
306        name: "Argument from Sunk Cost".into(),
307        category: SchemeCategory::Practical,
308        premises: vec![
309            PremiseSlot::new("action", "The committed action", SlotRole::Action),
310            PremiseSlot::new(
311                "commitment",
312                "The prior commitment that locks the agent in",
313                SlotRole::Property,
314            ),
315        ],
316        conclusion: ConclusionTemplate::positive(
317            "?action should be continued to honour ?commitment",
318            "continue_?action",
319        ),
320        critical_questions: vec![
321            CriticalQuestion::new(
322                1,
323                "Is ?commitment still binding given current circumstances?",
324                Challenge::PremiseTruth("commitment".into()),
325            ),
326            CriticalQuestion::new(
327                2,
328                "Does honouring ?commitment actually require continuing ?action?",
329                Challenge::RuleValidity,
330            ),
331        ],
332        metadata: SchemeMetadata {
333            citation: "Walton 2008 p.340".into(),
334            domain_tags: vec!["practical".into(), "sunk_cost".into()],
335            presumptive: true,
336            strength: SchemeStrength::Weak,
337        },
338    }
339}
340
341#[cfg(test)]
342mod tests {
343    use super::*;
344
345    #[test]
346    fn all_returns_seven_practical_schemes() {
347        let schemes = all();
348        assert_eq!(schemes.len(), 7);
349        assert!(
350            schemes
351                .iter()
352                .all(|s| s.category == SchemeCategory::Practical)
353        );
354    }
355
356    #[test]
357    fn negative_consequences_has_negated_conclusion() {
358        assert!(argument_from_negative_consequences().conclusion.is_negated);
359    }
360
361    #[test]
362    fn positive_consequences_has_non_negated_conclusion() {
363        assert!(!argument_from_positive_consequences().conclusion.is_negated);
364    }
365
366    #[test]
367    fn threat_scheme_has_three_premises() {
368        assert_eq!(argument_from_threat().premises.len(), 3);
369    }
370
371    #[test]
372    fn practical_ids_are_in_offset_range() {
373        for s in all() {
374            assert!(s.id.0 >= PRACTICAL_ID_OFFSET);
375            assert!(s.id.0 < PRACTICAL_ID_OFFSET + 100);
376        }
377    }
378}