<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ logic - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ logic - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:24:49 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/logic/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ The Logic, Philosophy, and Science of Software Testing – A Handbook for Developers ]]>
                </title>
                <description>
                    <![CDATA[ In an age of information overload, AI assistance, and rapid technological change, the ability to think clearly and reason soundly has never been more valuable. This handbook takes you on a journey from fundamental logical principles to their practica... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-logic-philosophy-and-science-of-software-testing-handbook-for-developers/</link>
                <guid isPermaLink="false">6851b75a6fd83aa331a8943b</guid>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ debugging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ logic ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Science  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Han Qi ]]>
                </dc:creator>
                <pubDate>Tue, 17 Jun 2025 18:43:38 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750176539544/965a99ef-8aad-467c-ae6b-4a144e2d1117.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In an age of information overload, AI assistance, and rapid technological change, the ability to think clearly and reason soundly has never been more valuable.</p>
<p>This handbook takes you on a journey from fundamental logical principles to their practical applications in software development, scientific reasoning, and critical thinking.</p>
<p>Whether you're a high school student learning to think more clearly, a professional debugging complex systems, or simply someone curious about how sound reasoning works, this handbook provides tools for sharper, more reliable thinking.</p>
<h2 id="heading-what-well-cover">What We’ll Cover:</h2>
<h3 id="heading-part-i-foundational-theory"><strong>Part I: Foundational Theory</strong></h3>
<p>We start with the bedrock of formal logic – understanding implications, truth tables, and the core rules of reasoning.</p>
<p>You'll learn the scaffolding for everything that follows:</p>
<ul>
<li><p>How "if-then" statements actually work (spoiler: it's not always intuitive!)</p>
</li>
<li><p>The power of truth tables to map all possible scenarios</p>
</li>
<li><p>Why some arguments are valid while others are logical fallacies</p>
</li>
<li><p>The elegant relationship between <strong>Modus Ponens, Modus Tollens, and Contrapositives</strong></p>
</li>
</ul>
<h3 id="heading-part-ii-practical-applications"><strong>Part II: Practical Applications</strong></h3>
<p>Here's where logic comes alive in tangible ways:</p>
<p><strong>In Software Development:</strong></p>
<ul>
<li><p>How debugging mirrors logical reasoning, and why your tests might be lying to you</p>
</li>
<li><p>The logic behind Test-Driven Development and Mutation Testing</p>
</li>
</ul>
<p><strong>In Scientific Thinking:</strong></p>
<ul>
<li><p>Karl Popper's falsification principle and why it matters beyond academia</p>
</li>
<li><p>How <strong>Hypothesis Testing</strong> is just statistics meets <strong>Modus Tollens</strong></p>
</li>
</ul>
<p><strong>In Everyday Reasoning:</strong></p>
<ul>
<li><p>Spotting logical fallacies in arguments, media, and your thinking</p>
</li>
<li><p>The art of considering multiple causal paths instead of jumping to conclusions</p>
</li>
</ul>
<h3 id="heading-part-iii-philosophical-depths"><strong>Part III: Philosophical Depths</strong></h3>
<p>The final section confronts the beautiful complexity of applying pure logic to an impure world:</p>
<ul>
<li><p>Why perfect "<strong>if-and-only-if</strong>" relationships are the goal but rarely achievable</p>
</li>
<li><p>How modern software systems hide their complexity</p>
</li>
<li><p>The butterfly effect of bugs and why root cause analysis is often harder than it seems</p>
</li>
<li><p>Formal verification tools: from <strong>Prolog</strong> to <strong>Coq</strong> to <strong>TLA+</strong></p>
</li>
</ul>
<h2 id="heading-what-youll-gain">What You'll Gain</h2>
<h3 id="heading-for-students"><strong>For Students:</strong></h3>
<ul>
<li><p><strong>Critical thinking superpowers</strong>: Learn to spot flawed reasoning in arguments, social media, and news</p>
</li>
<li><p><strong>Academic advantage</strong>: These concepts appear in debates, philosophy, computer science, mathematics, and statistics</p>
</li>
</ul>
<h3 id="heading-for-software-engineers"><strong>For Software Engineers:</strong></h3>
<ul>
<li><p><strong>Debugging mastery</strong>: <em>Modus Tollens</em> for debugging: "If the output is wrong, what could cause it?"</p>
</li>
<li><p><strong>Testing philosophy</strong>: Move beyond "make the tests pass" to "prove the code is correct"</p>
</li>
<li><p><strong>Problem analysis</strong>: Avoid jumping to solutions before understanding the real problem</p>
</li>
<li><p><strong>System design</strong>: Think more rigorously about failure modes and edge cases, evaluate cause-and-effect relationships in complex systems</p>
</li>
<li><p><strong>Communication and career growth</strong>: Present arguments more clearly and persuasively, gain logical thinking skills that separate senior engineers from juniors</p>
</li>
</ul>
<h3 id="heading-for-scientists"><strong>For Scientists:</strong></h3>
<ul>
<li><p><strong>Experimental design</strong>: Strengthen your understanding of hypothesis testing and falsifiability</p>
</li>
<li><p><strong>Peer review</strong>: Better evaluate the logical soundness of research claims</p>
</li>
<li><p><strong>Grant writing</strong>: Structure arguments more persuasively using solid logical foundations</p>
</li>
</ul>
<h2 id="heading-pre-requisites">Pre-requisites</h2>
<p>I’ll introduce code samples starting in the second half of the article, so knowing a programming language would be helpful. The concepts in this article are programming language-agnostic, but I’ve used Python throughout for readability.</p>
<p>No prior formal logic or philosophy background is strictly necessary, but the following will let you reap the most benefits from this article:</p>
<ul>
<li><p>Experience in testing and debugging during software development.</p>
</li>
<li><p>Know what REPL (Read-Evaluate-Print-Loop) is if you want to try the Proof Assistants.</p>
</li>
<li><p>Knowledge of logical operators (NOT, AND, OR), and the fact that they take 1 or 2 boolean values as input and return a single boolean value as output.</p>
</li>
<li><p>Basic Algebraic Thinking: representing statements as variables (P, Q), the concept of NOT (¬) as an inversion of statements, and the concept that different input combinations can reach the same output.</p>
</li>
<li><p>Exposure to deductive reasoning, where inferences are made based on some facts, and fallacies, which are some ways arguments can be flawed.</p>
</li>
<li><p>Willingness to engage in conceptual back-and-forth between concrete English examples and abstract logical symbols.</p>
</li>
<li><p>Holding possibly conflicting ideas between the ideal logic world and the impure real world.</p>
</li>
<li><p>Openness to challenging intuition and following logical rules before applying your real-world experience.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-an-introduction-to-logic">An Introduction to Logic</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-truth-tables-mapping-all-possibilities">Truth Tables: Mapping All Possibilities</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-contrapositives-modus-ponens-modus-tollens">Contrapositives, Modus Ponens, Modus Tollens</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-origin-of-pq-science-and-reality">The Origin of P⟹Q: Science and Reality</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-revisiting-argument-forms-valid-inferences-and-common-fallacies">Revisiting Argument Forms: Valid Inferences and Common Fallacies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-denying-the-antecedent-a-database-example">Denying the Antecedent: A Database Example</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-assigning-real-world-meanings-to-logic">Assigning Real-World Meanings to Logic</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-applying-logic-to-software-testing">Applying Logic to Software Testing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-closer-look-at-testing">A Closer Look at Testing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-revisiting-the-four-statements-for-coding">Revisiting the Four Statements for Coding</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-missing-ingredient-if-and-only-if">The Missing Ingredient - If and Only If</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-mutation-testing-testing-the-tests">Mutation Testing: Testing the Tests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-toward-if-and-only-if-confidence">Toward If-and-Only-If Confidence</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-challenges">Real-World Challenges</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-glimmers-of-hope-tools-and-practices-for-clarity">Glimmers of Hope: Tools and Practices for Clarity</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-power-of-falsification-in-testing">The Power of Falsification in Testing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-proof-assistants">Proof Assistants</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-food-for-thought">Food for Thought</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-qed-the-enduring-power-of-logic-in-an-uncertain-world">Q.E.D.: The Enduring Power of Logic in an Uncertain World</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources">Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-glossary">Glossary</a></p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749064487021/b0404a1e-3257-4815-bc42-517b2ea955d0.jpeg" alt="man standing at edge of lake looking into the distance" class="image--center mx-auto" width="5056" height="3419" loading="lazy"></p>
<h2 id="heading-an-introduction-to-logic">An Introduction to Logic</h2>
<p>Imagine that the following statement is True:</p>
<p><strong>If you are a coding instructor, then you have a job.</strong></p>
<p>Now, do these make sense?</p>
<ol>
<li><p>You have no job, so you are not a coding instructor</p>
</li>
<li><p>You have a job, so you are a coding instructor</p>
</li>
<li><p>You are not a coding instructor, so you have no job</p>
</li>
</ol>
<h3 id="heading-interpretations">Interpretations</h3>
<p>Based on logic:</p>
<ul>
<li><p>Statement 1 is correct.</p>
</li>
<li><p>Statement 2 is wrong because you may have other jobs without being a coding instructor.</p>
</li>
<li><p>Statement 3 is wrong because you may or may not have a job, and as before, you may have other jobs without being a coding instructor.</p>
</li>
</ul>
<h3 id="heading-growing-complexity">Growing complexity</h3>
<p>These statements grow increasingly complex due to:</p>
<ul>
<li><p>Changing from 2 valid statements to 2 invalid conclusions</p>
</li>
<li><p>Moving from a clear job status (1, 2) to uncertainty about job existence or type (3).</p>
</li>
</ul>
<p>Let’s get familiar with some notation before seeing how <strong>Truth tables</strong> help manage this complexity.</p>
<h3 id="heading-notations">Notations</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Notation</td><td>Meaning</td><td>Example (if P="It's raining", Q="The ground is wet")</td></tr>
</thead>
<tbody>
<tr>
<td><strong>P, Q</strong></td><td>Propositions</td><td>P, Q</td></tr>
<tr>
<td><strong>⟹</strong></td><td>Implies / If...then...</td><td>P⟹Q ("If it's raining, then the ground is wet")</td></tr>
<tr>
<td><strong>¬</strong></td><td>Not</td><td>¬P ("It's not raining")</td></tr>
<tr>
<td><strong>∧</strong></td><td>And (conjunction)</td><td>P∧Q ("It's raining and the ground is wet")</td></tr>
<tr>
<td><strong>∨</strong></td><td>Or (disjunction)</td><td>P∨Q ("It's raining or the ground is wet")</td></tr>
<tr>
<td><strong>⟺</strong></td><td>If and only if (biconditional)</td><td>P⟺Q ("It's raining if and only if the ground is wet")</td></tr>
<tr>
<td>∴</td><td>Therefore</td><td>P ⟹ Q: If it's raining, then the ground is wet; P: It's raining; ∴ Q: <strong>Therefore</strong>, the ground is wet</td></tr>
</tbody>
</table>
</div><h2 id="heading-truth-tables-mapping-all-possibilities">Truth Tables: Mapping All Possibilities</h2>
<h3 id="heading-what-is-a-truth-table"><strong>What is a Truth Table?</strong></h3>
<p>A truth table is a powerful tool in logic that helps us determine the overall truth or falsity of a compound logical statement. It does this by systematically listing <strong>all possible combinations</strong> of truth values (True or False) for its individual component propositions.</p>
<p>For every way the "inputs" (our propositions like P and Q) can be true or false, the truth table shows you the precise "output" (the truth value of the entire logical statement, such as P⟹Q).</p>
<h3 id="heading-why-are-truth-tables-helpful"><strong>Why are Truth Tables Helpful?</strong></h3>
<p>Truth tables offer critical benefits for clear thinking:</p>
<ul>
<li><p><strong>Clarity and precision:</strong> They eliminate ambiguity by explicitly showing the outcome for every single scenario.</p>
</li>
<li><p><strong>Systematic analysis:</strong> They ensure no possible combination is missed, which is vital for sound reasoning.</p>
</li>
<li><p><strong>Foundation for understanding:</strong> They define how logical rules work, forming the bedrock for analyzing more complex arguments in any domain.</p>
</li>
</ul>
<h3 id="heading-how-to-read-our-first-truth-table"><strong>How to Read Our First Truth Table:</strong></h3>
<p>Let's examine the truth table for the implication P⟹Q ("If P then Q").</p>
<p>Each row represents a unique scenario, combining the truth values of P and Q to show the resulting truth value of P⟹Q.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>P</td><td>Q</td><td>P⟹Q (If P then Q)</td><td>Used In</td></tr>
</thead>
<tbody>
<tr>
<td>True</td><td>True</td><td>True</td><td>Modus Ponens ✅</td></tr>
<tr>
<td>True</td><td>False</td><td>False</td><td>Falsifiability 🚨</td></tr>
<tr>
<td>False</td><td>True</td><td>True</td><td>No Inference</td></tr>
<tr>
<td>False</td><td>False</td><td>True</td><td>Modus Tollens ✅</td></tr>
</tbody>
</table>
</div><p>Let's break down each row:</p>
<ul>
<li><p><strong>P and Q Columns:</strong> These show the input truth values (True or False) for our two propositions. Since each can be one of two values, we have 2×2 = 4 unique combinations, filling all four rows.</p>
</li>
<li><p><strong>P ⟹ Q Column:</strong> This is the output truth value of the "If P then Q" statement for each combination of inputs P and Q.</p>
<ul>
<li><p><strong>Row 1: P is True, Q is True.</strong></p>
<ul>
<li><p>If P is true <strong>(you are a coding instructor</strong>) and Q is also true <strong>(you have a job</strong>), then the implication P⟹Q is <strong>True</strong>. (The "If...then..." statement holds).</p>
</li>
<li><p>This row is key for <strong>Modus Ponens</strong>.</p>
</li>
</ul>
</li>
<li><p><strong>Row 2: P is True, Q is False</strong></p>
<ul>
<li><p>If P is true <strong>(you are a coding instructor</strong>) but Q is false <strong>(you have a job</strong>), then the implication P⟹Q is <strong>False</strong>. This is the only scenario that disproves an "if-then" statement.</p>
</li>
<li><p>This row is key for <strong>Falsifiability</strong>.</p>
</li>
</ul>
</li>
<li><p><strong>Row 3: P is False, Q is True.</strong></p>
<ul>
<li><p>If P is False <strong>(you are not a coding instructor)</strong> but Q is True <strong>(you have a job)</strong>, then the implication P⟹Q is still considered <strong>True</strong>. This can seem counter-intuitive.</p>
</li>
<li><p>The reason is that the implication statement <em>only</em> makes a claim about what happens when P is true. If P is false, the implication's claim isn't tested, so it is considered <a target="_blank" href="https://en.wikipedia.org/wiki/Vacuous_truth">vacuously true</a>.</p>
</li>
</ul>
</li>
<li><p><strong>Row 4: P is False, Q is False.</strong></p>
<ul>
<li><p>If P is False <strong>(you are not a coding instructor)</strong> and Q is False <strong>(you have no job)</strong>, then the implication P⟹Q is also considered <strong>True</strong>.</p>
</li>
<li><p>Similar to Row 3, since the initial condition (P) was false, the implication's truth value remains True, as it hasn't been disproven.</p>
</li>
<li><p>This row is key for <strong>Modus Tollens</strong>.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The "Used In" column serves as a preview of the specific logical arguments or concepts that rely on each row's behavior, which we will explore in detail later.</p>
<h3 id="heading-understanding-the-implication-pq-deeper">Understanding the Implication (P⟹Q) Deeper</h3>
<p>Most programmers are familiar with truth tables from logical operators like <strong>AND (∧)</strong>, <strong>OR (∨)</strong>, and <strong>NOT (¬)</strong>, where they define the output based on combinations of inputs.</p>
<p>The implication (P⟹Q) works similarly, its output is defined by the rules of propositional logic, not by any real-world causal relationship or your “common sense”. For any given pair of inputs for P and Q, the result of P⟹Q is fixed.</p>
<p>If this feels counter-intuitive, consider that mathematical logic, like any formal system, is built upon agreed-upon <strong>axioms</strong>. These basic accepted truths allow us to construct complex systems of ideas. If later found ineffective or contradictory, these axioms can be redefined, or a new system can be developed.</p>
<p>In formal logic, this implication is also defined as being logically equivalent to <strong>"NOT P OR Q" (¬P∨Q)</strong>.</p>
<p>This is the fundamental logical rule that dictates why, <strong>if P is False, P⟹Q is always True, regardless of Q's truth value</strong>. You can also understand this using the <strong>NOT P OR Q</strong> form.</p>
<ul>
<li><p>If P is False, that means NOT P is True.</p>
</li>
<li><p>Using the rules of Logical operation:</p>
<ul>
<li><p>True (Not P) OR True (Q) is True (<strong>NOT P OR Q</strong>)</p>
</li>
<li><p>True (Not P) OR False (Q) is True (<strong>NOT P OR Q</strong>)</p>
</li>
<li><p><strong>NOT P OR Q</strong> is True regardless of what Q is.</p>
</li>
</ul>
</li>
</ul>
<p>The above explains rows 3 and 4 of the truth table from the <strong>NOT P OR Q</strong> form. As an exercise, you can apply the inputs (P, Q) from the first two rows of the truth table to NOT P OR Q to arrive at the same results defined in the P⟹Q column.</p>
<p>This formal definition allows us to use implication to reason in powerful ways, not just in the "forward" direction (P⟹Q, leading to Modus Ponens), but also in a crucial "backward" direction.</p>
<p>This backward form (<strong>Contrapositive</strong>) involves swapping and negating the propositions (¬Q⟹¬P).</p>
<p>For example, if "If you are a coding instructor, then you have a job" is true, then it must also be true that "If you have no job (¬Q), then you are not a coding instructor (¬P). ".</p>
<p>This "backward" way of reasoning, which underpins Modus Tollens, is a powerful tool for inferring conclusions from observed outcomes.</p>
<p>We'll explore the <strong>Contrapositive</strong> and two argument forms (<strong>Modus Ponens, Modus Tollens</strong>) in detail next.</p>
<h2 id="heading-contrapositives-modus-ponens-modus-tollens">Contrapositives, Modus Ponens, Modus Tollens</h2>
<p>We've explored the fundamental implication (P⟹Q) and how truth tables reveal its behavior.</p>
<p>Now, we explore reasoning tools that build upon this foundation: <strong>Modus Ponens</strong>, <strong>Modus Tollens</strong>, and the concept of <strong>Contrapositives</strong>. These are bedrock principles of valid argument and efficient logical thought.</p>
<h3 id="heading-what-is-logical-equivalence">What is Logical Equivalence?</h3>
<p>Before we dive into these specific concepts, let's clarify what <strong>logical equivalence</strong> means. Two statements are <strong>logically equivalent</strong> if they always have the same truth value under all possible circumstances. In simpler terms, if one statement is true, the other is <em>always</em> true. If one is false, the other is <em>always</em> false. They are, in essence, different ways of saying the same logical thing.</p>
<p>Understanding logical equivalence is incredibly useful. It:</p>
<ul>
<li><p><strong>Simplifies logic:</strong> It allows us to substitute one statement for another without changing the truth of an argument, which simplifies complex proofs and reasoning.</p>
</li>
<li><p><strong>Reduces complexity:</strong> In fields like circuit design, it can lead to fewer physical gates.</p>
</li>
<li><p><strong>Maintains software correctness:</strong> In programming, it helps maintain code's correctness during refactoring and debugging, especially when simplifying conditional statements, by ensuring the transformed code still behaves identically to the original under all conditions.</p>
</li>
</ul>
<h3 id="heading-the-contrapositive-an-equivalent-implication">The Contrapositive: An Equivalent Implication</h3>
<p>One of the most important logical equivalences involves the <strong>Contrapositive</strong> of an implication. The contrapositive of an "If P then Q" (P⟹Q) statement is <strong>"If not Q, then not P"</strong> (¬Q⟹¬P).</p>
<p>You might intuitively question how "<strong>If P then Q</strong>" could be logically the same as "<strong>If not Q then not P</strong>." Let's demonstrate this using a truth table.</p>
<p>We'll start with our familiar P and Q columns and the P⟹Q implication. Then, we'll add columns for ¬P (Not P) and ¬Q (Not Q), and finally, the implication for the contrapositive, ¬Q⟹¬P.</p>
<p>Let's look at how the truth table explicitly shows this equivalence:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747584857181/2732a798-da1d-48d9-aa92-c1ca3459b169.png" alt="Truth Table of columns P, Q, P->Q, not P, not Q, not Q -> not P" class="image--center mx-auto" width="1042" height="325" loading="lazy"></p>
<h3 id="heading-explanation-of-the-table">Explanation of the table</h3>
<ol>
<li><p><strong>P, Q, P ⟹ Q (Columns 1-3):</strong> These are our standard propositions and the implication we've already defined.</p>
</li>
<li><p><strong>¬P (Column 4):</strong> This column simply shows the negation (opposite truth value) of the P column. If P is True, ¬P is False, and vice-versa.</p>
</li>
<li><p><strong>¬Q (Column 5):</strong> Similarly, this column shows the negation of the Q column.</p>
</li>
<li><p><strong>¬Q ⟹ ¬P (Column 6):</strong> This is the contrapositive. We apply the same rules for implication that we learned earlier, but now using ¬Q as our "if" part and ¬P as our "then" part. For example, in Row 2, ¬Q is True and ¬P is False. According to the implication rule (True ⟹ False yields False), the result for ¬Q⟹¬P is False.</p>
</li>
<li><p><strong>The Proof of Equivalence:</strong> Now, compare <strong>Column 3 (P⟹Q)</strong> with <strong>Column 6 (¬Q⟹¬P)</strong>. You'll notice that for every single row, their truth values are identical! When P⟹Q is True, ¬Q⟹¬P is also True. When P⟹Q is False, ¬Q⟹¬P is also False. This perfectly illustrates why they are <strong>logically equivalent</strong>.</p>
</li>
</ol>
<p>So, "If you are a coding instructor, then you have a job" (P⟹Q) is logically the same as saying "If you have no job, then you are not a coding instructor" (¬Q⟹¬P). They convey the same information about the relationship between being a coding instructor and having a job.</p>
<h3 id="heading-how-modus-ponens-and-modus-tollens-relate-to-implication">How Modus Ponens and Modus Tollens Relate to Implication</h3>
<p>Having defined logical equivalence and the contrapositive, we can now precisely understand two of the most fundamental and valid forms of deductive argument: <strong>Modus Ponens</strong> and <strong>Modus Tollens</strong>. Both of these argument forms rely on a core premise that an implication (P⟹Q) is true, and then use additional information to draw a valid conclusion.</p>
<ol>
<li><p><strong>Modus Ponens (Affirming the Antecedent):</strong> This is often considered the most intuitive and direct form of logical inference. It works in the "forward" direction of the implication.</p>
<ul>
<li><p><strong>Premise 1:</strong> We are given that the implication is true: If P, then Q (P⟹Q).</p>
</li>
<li><p><strong>Premise 2:</strong> We are also given that the "if" part, the antecedent, is true: P is true.</p>
</li>
<li><p><strong>Conclusion:</strong> Therefore, we can validly infer that the "then" part, the consequent, must also be true: Q is true.</p>
</li>
</ul>
</li>
</ol>
<p>    <em>Example:</em></p>
<ul>
<li><p>Premise 1: If it is raining (P), then the ground is wet (Q).</p>
</li>
<li><p>Premise 2: It is raining (P).</p>
</li>
<li><p>Conclusion: Therefore, the ground is wet (Q).</p>
</li>
</ul>
<p>    This directly corresponds to <strong>Row 1 (True, True)</strong> of our truth table for P⟹Q.</p>
<ol start="2">
<li><p><strong>Modus Tollens (Denying the Consequent):</strong> This argument form works in the "backward" direction and relies directly on the logical equivalence of an implication and its contrapositive.</p>
<ul>
<li><p><strong>Premise 1:</strong> We are given that the implication is true: If P, then Q (P⟹Q).</p>
</li>
<li><p><strong>Premise 2</strong>: We are also given that the "then" part, the consequent, is false: Not Q (¬Q).</p>
</li>
<li><p><strong>Conclusion</strong>: Therefore, we can validly infer that the "if" part, the antecedent, must also be false: Not P (¬P).</p>
</li>
</ul>
</li>
</ol>
<p>    <em>Example:</em></p>
<ul>
<li><p>Premise 1: If it is raining (P), then the ground is wet (Q).</p>
</li>
<li><p>Premise 2: The ground is <strong>not</strong> wet (¬Q).</p>
</li>
<li><p>Conclusion: Therefore, it is <strong>not</strong> raining (¬P).</p>
</li>
</ul>
<p>    Modus Tollens is valid because if P⟹Q is true, its contrapositive (¬Q⟹¬P) must also be true. Applying Modus Ponens to this contrapositive (with ¬Q as our second premise) directly leads to the conclusion ¬P. This corresponds to <strong>Row 4 (False, False)</strong> of our original truth table for P⟹Q, where P and Q are both false but the implication is still true.</p>
<p>These two argument forms are central to rigorous deductive reasoning, allowing us to draw certain conclusions based on the truth of implications and related facts.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749063972374/e3eaf8a6-8eb1-4fa2-9e97-703b547a81bd.jpeg" alt="Title Page of Book by Charles Darwin: On the Origin of Species" class="image--center mx-auto" width="4473" height="2982" loading="lazy"></p>
<h2 id="heading-the-origin-of-pq-science-and-reality">The Origin of P⟹Q: Science and Reality</h2>
<p>In science, hypotheses often take the form "<strong>If P, then Q</strong>" where P is a cause and Q is its predicted effect –for example, "If a drug is given (P), then symptoms improve (Q)."</p>
<p>Ideally, P is controllable, as in experimental studies, but even in observational studies, P must be clearly defined and measurable.</p>
<p>Each experiment yields one observation, reflecting one of four possible truth-value combinations of P and Q.</p>
<h3 id="heading-the-falsifying-case-in-science-and-logic">The Falsifying Case in Science and Logic</h3>
<p>Each experiment produces a single observation – one of the four possible combinations of P and Q.</p>
<ul>
<li><p>If P=True, Q=False is observed (row 2 of the truth table), the hypothesis is <strong>falsified</strong></p>
</li>
<li><p>In all other cases, the hypothesis is <strong>not falsified</strong> (yet)</p>
</li>
</ul>
<p>Thus:</p>
<ul>
<li><p>If all observations fall in the 3 truth-preserving rows, the hypothesis remains viable.</p>
</li>
<li><p>If at least one experiment yields P=True, Q=False, we either:</p>
<ul>
<li><p>Conclude falsification, or</p>
</li>
<li><p>Re-examine the experiment and attempt replication before accepting falsification.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-the-power-of-the-falsifying-case">The Power of the Falsifying Case</h3>
<h4 id="heading-in-the-logical-world">In the Logical World</h4>
<p>The falsifying case is not useful for inference with Modus Ponens or Modus Tollens because these two argument forms require starting with <strong>P⟹Q = True</strong>. I’ll explain both arguments in detail later.</p>
<p>But the falsifying case is useful for showing counterexamples to disprove the implication, or proof by contradiction.</p>
<h4 id="heading-in-the-real-scientific-world">In the Real Scientific world</h4>
<p>The falsifying case embodies <strong>Falsifiability</strong> – a crucial concept in Science.</p>
<blockquote>
<p>In so far as a scientific statement speaks about reality, it must be falsifiable: and in so far as it is not falsifiable, it does not speak about reality.</p>
<p><strong>— Karl R. Popper, The Logic of Scientifc Discovery</strong></p>
</blockquote>
<p>Scientific theories come about through hypotheses that are continually tested and survive attempts at falsification.</p>
<h3 id="heading-popperian-falsification-and-hypothesis-testing">Popperian Falsification and Hypothesis Testing</h3>
<p>These two approaches, one philosophical and one statistical, are distinct but complementary in the scientific method.</p>
<ul>
<li><p><strong>Popperian Falsification</strong> starts with a scientific hypothesis (for example, "P has an effect on Q"). Its core aim is to actively seek evidence that would disprove this hypothesis. If such disproving evidence is found, the hypothesis is falsified.</p>
</li>
<li><p><strong>Statistical Hypothesis Testing</strong> begins with a null hypothesis (H0​) (for example, "P has no effect on Q"). Its goal is to determine if the collected data provides sufficiently extreme evidence to reject this null hypothesis.</p>
</li>
</ul>
<p>If the null hypothesis is rejected, it provides statistical support for the alternative hypothesis (that P <em>does</em> have an effect on Q). This statistically supported hypothesis then becomes a stronger candidate, continually subjected to further Popperian attempts at falsification through new experiments and observations.</p>
<h3 id="heading-the-nuance-implication-is-not-causality">The Nuance: Implication is Not Causality</h3>
<p>P⟹Q does <strong>not</strong> inherently imply that P causes Q.</p>
<p>Consider these examples:</p>
<ul>
<li><p>"If the fire alarm is sounding, then there is smoke." The alarm doesn't <em>cause</em> the smoke.</p>
</li>
<li><p>"If a colleague screams during code review, then the code is bad." Does the screaming <em>cause</em> the bad code, or merely reveal it? (Perhaps sometimes both! 😰)</p>
</li>
</ul>
<p><strong>Causality</strong> is a real-world concept crucial for making informed decisions, predicting outcomes, and inferring the underlying reasons for events.</p>
<p>It's often central to predictive modeling and supervised learning in data science, where the target variable is the effect and the predictors are proposed causes. A common pitfall here is <strong>data leakage</strong>, where predictors are inadvertently influenced by (or are themselves effects of) the target, violating the causal assumption.</p>
<p>Logic, however, doesn't model time, mechanisms, or interventions. It only cares about <strong>truth values and formal structure</strong>. Logic defines what is true based on premises, not what <em>makes</em> something true in a causal sense.</p>
<h2 id="heading-revisiting-argument-forms-valid-inferences-and-common-fallacies">Revisiting Argument Forms: Valid Inferences and Common Fallacies</h2>
<p>We've now established the rules of implication, understood logical equivalence, and learned about two powerful, valid argument forms: <strong>Modus Ponens</strong> and <strong>Modus Tollens</strong>. But when we try to reason using "if-then" statements, it's easy to fall into common logical traps.</p>
<p>In this section, we'll systematically revisit the four common ways we might try to draw conclusions from an implication <strong>P⟹Q (If you are a coding instructor, then you have a job)</strong> introduced at the start of the handbook.</p>
<p>Two are valid arguments (Modus Ponens and Modus Tollens), and two are common logical fallacies. Understanding the differences is crucial for sound reasoning.</p>
<p>First, let's quickly define the parts of an "if-then" condition:</p>
<ul>
<li><p><strong>Antecedent:</strong> The "if" part of the condition (P).</p>
</li>
<li><p><strong>Consequent:</strong> The "then" part of the condition (Q).</p>
</li>
</ul>
<p>Now, let's examine these four argument forms, using our knowledge of truth tables and the coding instructor example.</p>
<h3 id="heading-affirming-the-antecedent-modus-ponens">Affirming the Antecedent (Modus Ponens)</h3>
<p>This is the first valid argument form we discussed. It's called "affirming the antecedent" because it asserts the truth of the "if" part (the antecedent, P) to conclude the "then" part (the consequent, Q).</p>
<ul>
<li><p><strong>Argument Form:</strong></p>
<ol>
<li><p>If P, then Q (P⟹Q)</p>
</li>
<li><p>P is true.</p>
</li>
<li><p>Therefore, Q is true.</p>
</li>
</ol>
</li>
<li><p><strong>Examples:</strong></p>
<ul>
<li><p>You are a coding instructor (P), so you have a job (Q).</p>
</li>
<li><p>You provided invalid input data (P), so the code will show an error (Q).</p>
</li>
</ul>
</li>
<li><p><strong>Interpretation:</strong> This argument directly aligns with <strong>Row 1 (P=True, Q=True)</strong> of our truth table, where the implication holds true. It's often the most intuitive form of logical deduction. In programming, it's natural to expect bad input to lead to error messages if the code is designed correctly.</p>
</li>
</ul>
<h3 id="heading-denying-the-consequent-modus-tollens">Denying the Consequent (Modus Tollens)</h3>
<p>This is the second valid argument form. It's called "denying the consequent" because it asserts the falsity of the "then" part (the consequent, ¬Q) to conclude the falsity of the "if" part (the antecedent, ¬P). As we learned, Modus Tollens derives its validity from the logical equivalence of P⟹Q and its contrapositive (¬Q⟹¬P).</p>
<ul>
<li><p><strong>Argument Form:</strong></p>
<ol>
<li><p>If P, then Q (P⟹Q)</p>
</li>
<li><p>Not Q is true (¬Q).</p>
</li>
<li><p>Therefore, Not P is true (¬P).</p>
</li>
</ol>
</li>
<li><p><strong>Examples:</strong></p>
<ul>
<li><p>You have no job (¬Q), so you are not a coding instructor (¬P).</p>
</li>
<li><p>There are no error messages (¬Q), so the input data is valid (¬P)</p>
</li>
</ul>
</li>
<li><p><strong>Interpretation:</strong> This argument corresponds to <strong>Row 4 (P=False, Q=False)</strong> of our truth table, where P⟹Q is true, and both P and Q are false. This form of reasoning is critical for skillful debugging, allowing you to infer reasonably true conclusions about the cause (P) from observations of the outcome (Q), assuming your program logic (P⟹Q) holds true.</p>
</li>
</ul>
<h3 id="heading-affirming-the-consequent-fallacy">Affirming the Consequent (Fallacy)</h3>
<p>Now we move to the common pitfalls. This is an <strong>invalid argument form</strong> where we attempt to conclude that the antecedent (P) is true simply because the consequent (Q) is true. It's a fallacy because the truth of Q does not guarantee the truth of P, as Q could have been caused by something other than P.</p>
<ul>
<li><p><strong>Argument Form (Invalid):</strong></p>
<ol>
<li><p>If P, then Q (P⟹Q)</p>
</li>
<li><p>Q is true.</p>
</li>
<li><p>Therefore, P is true. (**Incorrect inference!**🚨)</p>
</li>
</ol>
</li>
<li><p><strong>Examples:</strong></p>
<ul>
<li><p>You have a job (Q), so you are a coding instructor (P).</p>
<ul>
<li>Incorrect: You could have many other jobs.</li>
</ul>
</li>
<li><p>The code showed an error (Q), so you provided invalid data (P).</p>
<ul>
<li>Incorrect: Other things besides invalid data can cause errors.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Interpretation:</strong> This fallacy highlights the difference between a one-to-one and a one-to-many relationship. Looking at our truth table, when P⟹Q is True and Q is True, P could be <strong>True (Row 1)</strong> or <strong>False (Row 3)</strong>. The argument mistakenly concludes that P must always be True. The uncertainty arises because observing Q as True doesn't uniquely point to P as the cause – there could be many other reasons or paths that lead to Q.</p>
<ul>
<li>Think of walking down a forest path, unaware that another trail has merged into yours from behind you. When retracing your steps in reverse, you encounter a split (Q) at that merge and feel disoriented, unsure which path leads back to your start point (P). Just as multiple paths can converge on the same point, multiple causes can produce the same outcome.</li>
</ul>
</li>
</ul>
<h3 id="heading-denying-the-antecedent-fallacy">Denying the Antecedent (Fallacy)</h3>
<p>This is another <strong>invalid argument form</strong>. Here, we attempt to conclude that the consequent (Q) is false simply because the antecedent (P) is false. It's a fallacy because P being false does not guarantee that Q will also be false. Q could still be true for other reasons, or the implication might not cover all scenarios where Q occurs.</p>
<ul>
<li><p><strong>Argument Form (Invalid):</strong></p>
<ol>
<li><p>If P, then Q (P⟹Q)</p>
</li>
<li><p>Not P is true (¬P).</p>
</li>
<li><p>Therefore, Not Q is true (¬Q). (**Incorrect inference!**🚨)</p>
</li>
</ol>
</li>
<li><p><strong>Examples:</strong></p>
<ul>
<li><p>You are not a coding instructor (¬P), so you have no job (¬Q).</p>
<ul>
<li>Incorrect: You could have a different job.</li>
</ul>
</li>
<li><p>You provided valid data (¬P), so you have no error (¬Q).</p>
<ul>
<li>Incorrect: Valid data doesn't guarantee no error. Other factors like network issues, memory leaks, or non-idempotent operations can still cause errors.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Interpretation:</strong> Similar to Affirming the Consequent, this fallacy stems from incorrectly assuming a unique relationship. From our truth table, when P⟹Q is True and P is False, Q could be <strong>True (Row 3)</strong> or <strong>False (Row 4)</strong>. The argument mistakenly concludes Q must always be False.</p>
</li>
</ul>
<p>Both of these fallacies (<strong>Affirming the Consequent</strong> and <strong>Denying the Antecedent</strong>) creep into our thinking when we prematurely assume a single cause for an effect. In complex real-world systems, many factors can lead to an outcome, and narrowing your thinking too soon can lead to missed bugs or incorrect conclusions.</p>
<h3 id="heading-fallacies-and-implication-a-prerequisite">Fallacies and Implication: A Prerequisite</h3>
<p>Both the fallacy of affirming the consequent and denying the antecedent assume the underlying implication (P⟹Q) is true.</p>
<p>If this implication is false from the start, there's no logical argument to be made, and thus, no fallacy to speak of.</p>
<h3 id="heading-exercise-identifying-an-argument-form">Exercise: Identifying an Argument Form</h3>
<p>Which of the 4 forms of argument is this?</p>
<ul>
<li><strong>Penguins can’t fly. I can’t fly. Therefore, I’m a penguin.</strong></li>
</ul>
<p><em>Hint: Rephrase the first statement into an if-then form</em>.</p>
<h2 id="heading-denying-the-antecedent-a-database-example">Denying the Antecedent: A Database Example</h2>
<p>We just saw that Denying the Antecedent is a logical fallacy, meaning that even if the initial implication (P⟹Q) is true, concluding ¬Q from ¬P is not a valid inference. To make this abstract concept concrete, and to illustrate why this fallacy can be particularly dangerous in real-world systems like software, let's explore a practical example involving a database.</p>
<p>The implication: <strong>If the database is down (P), we’ll see a connection timeout error (Q).</strong></p>
<p>Now, applying the fallacy of Denying the Antecedent, we might incorrectly conclude: <strong>If the database is not down (¬P), we will not see a connection timeout error (¬Q). ❌</strong></p>
<p>But even if the database itself is perfectly operational and "not down," you might still encounter a connection timeout error. This could happen due to a variety of other, independent reasons, such as:</p>
<ul>
<li><p>Network problems</p>
</li>
<li><p>Firewall rules</p>
</li>
<li><p>The database is up but extremely slow</p>
</li>
<li><p>The query engine is stuck</p>
</li>
</ul>
<p>This specific example of multiple potential causes for a "timeout" highlights a broader, critical skill in software development: <strong>thorough case analysis</strong>.</p>
<p>This is precisely why technical assessments, especially in areas like algorithms and system design, frequently demand that you consider exhaustive possibilities. For instance, you are often asked to handle <strong>base and recursive cases in dynamic programming</strong>, or to ensure <strong>mutually exclusive and collectively exhaustive coverage when grouping multiple scenarios in problems like interval merging.</strong></p>
<p>Such strong case analysis is vital for minimizing bugs and cultivating an open-minded approach to considering multiple causal paths, driven by experience, curiosity, and a dedication to craftsmanship.</p>
<p>But even perfect case analysis doesn't guarantee a correct implementation. Weak language mastery or mistaken assumptions can still lead to errors, making tests a crucial last line of defense.</p>
<p>Before jumping into applying logic to software testing, let’s practice our agility in conceptually switching between real-world concepts in English and symbols in logic.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750012280729/731cd405-1a5c-45c1-8d16-9e6b28837979.jpeg" alt="kitten in front of computer screen full of code" class="image--center mx-auto" width="6000" height="4000" loading="lazy"></p>
<h2 id="heading-assigning-real-world-meanings-to-logic">Assigning Real-World Meanings to Logic</h2>
<p>We must define what P, Q, and P⟹Q refer to when applying logical theory to real-world concepts.</p>
<p>How we define these variables affects our truth tables.</p>
<p>For example:</p>
<ul>
<li><p>If <strong>P means "valid input,"</strong> then ¬P means "invalid input."</p>
</li>
<li><p>If <strong>P means "invalid input,"</strong> then ¬P means "valid input."</p>
</li>
</ul>
<p>Imagine we define <strong>P = "Good input"</strong> and <strong>Q = "No Error."</strong></p>
<ul>
<li><p>When testing the <strong>happy path</strong>, we are verifying that the implication <strong>P⟹Q (If input is good, then no error)</strong> holds true.</p>
</li>
<li><p>When testing the <strong>unhappy path</strong> (mutation testing, more details later), we are verifying that <strong>¬P⟹¬Q (If input is not good, then an error occurs)</strong> holds true.</p>
</li>
</ul>
<p>In any test, a failure indicates that the tested implication is false. This warrants investigation into whether the issue lies with the specification's interpretation, the implementation, or even the test itself.</p>
<h2 id="heading-applying-logic-to-software-testing">Applying Logic to Software Testing</h2>
<p>Software development relies on constructing systems that behave predictably. <strong>Software testing</strong> is our primary tool for validating these behaviors. At its core, testing is a process deeply rooted in logical implications, where we propose a hypothesis about our code and then run an experiment (the test) to check its truth.</p>
<p>A test case is carefully designed to evaluate a specific piece of code. This involves:</p>
<ol>
<li><p><strong>Setting up Preconditions and Inputs:</strong> Before executing the code under test, we meticulously establish a specific environment and provide particular inputs. This includes:</p>
<ul>
<li><p><strong>Function/Method Arguments:</strong> The precise values passed into the code being tested.</p>
</li>
<li><p><strong>System State:</strong> Setting up relevant data in a database, preparing the content of a file system, configuring an object's instance variables, or dictating the responses of external services (often through "mocks" or "stubs").</p>
</li>
<li><p><strong>Environmental Factors:</strong> Controlling elements like the current time, specific network conditions, or user permissions relevant to the code's execution. This precise setup ensures that the code runs under defined conditions, allowing us to evaluate its behavior consistently.</p>
</li>
</ul>
</li>
</ol>
<p>Once the setup is complete, the code under test is executed, and its output or behavior is observed. This observation is then compared against an <strong>expected result</strong>.</p>
<p>To precisely analyze test outcomes, let's establish our specific logical mapping:</p>
<ul>
<li><p><strong>P: The code under test is correct for the specific scenario defined by the test.</strong> This refers to the <em>actual, objective state</em> of the code's internal logic and implementation when presented with the test's preconditions and inputs. If P is True, the code is without defect for this case. If P is False, there is a bug or deviation.</p>
</li>
<li><p><strong>Q: The test passes.</strong> This means the actual output or behavior observed from the code precisely matches the expected outcome defined in our test case. If they do not match, the test fails.</p>
</li>
<li><p><strong>P⟹Q: If the code under test is correct for this specific scenario, then the test will pass.</strong> In pure propositional logic, the truth value of P⟹Q is indeed defined by the truth values of P and Q. But in the context of software testing, P⟹Q represents our <strong>hypothesis or desired specification</strong> for how the code <em>should</em> behave. We don't directly "know" P's truth value beforehand. Instead, the test's execution provides empirical data (the actual Q) that allows us to <strong>evaluate whether this hypothesis holds true in practice</strong>, and thereby infer the actual state of P.</p>
</li>
</ul>
<p>Understanding this mapping is vital for interpreting test results. Let's examine the different outcomes of a test run, referencing the truth table for P⟹Q:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750280931102/bc300c03-ce17-456d-9a7e-47c8e649cfd6.png" alt="Truth table - explained in the text below" width="2431" height="1309" loading="lazy"></p>
<ul>
<li><p><strong>Row 1: P is True (Code is correct), Q is True (Test passes)</strong></p>
<ul>
<li><p><strong>Interpretation in Testing: Ideal State/Validation</strong></p>
<ul>
<li><p>This is the desired outcome and strengthens our confidence that the code adheres to its specification.</p>
</li>
<li><p>This scenario directly confirms the truth of our hypothesis (P⟹Q).</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Row 2: P is True (Code is correct), Q is False (Test fails)</strong></p>
<ul>
<li><p><strong>Interpretation in Testing: Logical Contradiction / Falsification of Hypothesis</strong></p>
<ul>
<li><p>This row means our overall hypothesis P⟹Q is <em>false</em> for this specific instance.</p>
</li>
<li><p>This demands investigation: either our initial assumption that P <em>was</em> True (meaning the code was correct) is wrong (i.e., there's an actual bug, so P is actually False), or the test itself is flawed (its inputs/expectations are incorrect), or the specification is wrong.</p>
</li>
<li><p>This is where rethinking of the P⟹Q hypothesis itself happens.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Row 3: P is False (Code is incorrect), Q is True (Test passes)</strong></p>
<ul>
<li><p><strong>Interpretation in Testing: False Positive / Inadequate Test</strong></p>
<ul>
<li><p>This is a problematic scenario. It implies the test is not robust enough to detect the defect in the code, or the test's expectation is flawed.</p>
</li>
<li><p>While P⟹Q remains true vacuously, this outcome is misleading and means the test is not effectively verifying code correctness.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Row 4: P is False (Code is incorrect), Q is False (Test fails)</strong></p>
<ul>
<li><p><strong>Interpretation in Testing: Bug Found / Confirmation of Incorrectness</strong></p>
<ul>
<li><p>This is a beneficial outcome, as the test has successfully identified a defect.</p>
</li>
<li><p>When P is truly False, P⟹Q is vacuously true.</p>
</li>
<li><p>This row can represent either a known, intended 'P is False' state (e.g., TDD Red phase) or the <em>actual state discovered</em> via deduction (explained below in Scenario 1).</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="heading-note-on-this-contextualized-truth-table-and-probabilistic-nature"><strong>Note on this Contextualized Truth Table and Probabilistic Nature</strong></h3>
<p>This truth table differs from a purely abstract logical truth table by being explicitly contextualized for software testing.</p>
<ul>
<li><p><strong>Specific Definitions:</strong> Unlike a generic P and Q, here they have precise meanings within the domain of code correctness and test outcomes.</p>
</li>
<li><p><strong>"Interpretation in Testing" Column:</strong> This is the key distinguishing feature. It translates the raw logical outcomes of (P, Q, and P⟹Q) into actionable insights and common debugging/development scenarios for software engineers. It explains <em>what it means</em> when a particular row is observed in the context of testing.</p>
</li>
<li><p><strong>Probabilistic Confidence:</strong> While formal logic operates in binary (True/False), real-world software testing often involves <strong>probabilistic confidence</strong>. A test doesn't provide absolute logical proof of correctness (for example, a passing test doesn't guarantee P is 100% True due to the possibility of undiscovered bugs or false positives). Instead, test results <em>increase our confidence</em> that the code is correct, or <em>provide strong evidence</em> that it is incorrect. Testing is fundamentally about reducing uncertainty and increasing the probability that our code functions as intended.</p>
</li>
</ul>
<p>Let's now explore how these logical outcomes are interpreted in two common testing scenarios:</p>
<h3 id="heading-scenario-1-debugging-an-unexpected-defect-applying-modus-tollens">Scenario 1: Debugging an Unexpected Defect (Applying Modus Tollens)</h3>
<p>This scenario occurs when a test that was previously passing, or a newly written test that we strongly trust as a precise and correct specification, unexpectedly fails. In this context, we assume the validity of the implication P⟹Q for this specific test case, treating it as an unbreakable rule for how correct code <em>should</em> behave.</p>
<ol>
<li><p><strong>Our Core Premise (Trusted Specification):</strong> We operate under the assumption that the implication "P⟹Q" ("If the code is correct for this scenario, then the test passes") is <strong>True</strong> for this specific test. Our confidence stems from the test's meticulous design, its history of passing, or its role in a well-established regression suite.</p>
</li>
<li><p><strong>Test Execution and Observation:</strong> We run the test, which has its preconditions and inputs set.</p>
<ul>
<li><p><strong>If the Test Fails (Q is False):</strong> This is the key observation. Since we <strong>trust our premise that P⟹Q is True</strong>, and we observe ¬Q (the test fails), we are logically compelled to deduce that our initial belief about P (the code being correct for this scenario) must be false.</p>
<ul>
<li><p><strong>Application of Modus Tollens:</strong></p>
<ul>
<li><p>Premise 1: If the code is correct for this scenario (P), then the test passes (Q). (P⟹Q, assumed true as a trusted specification).</p>
</li>
<li><p>Premise 2: The test did not pass (¬Q).</p>
</li>
<li><p>Conclusion: Therefore, the <strong>code is not correct for this scenario (¬P).</strong></p>
</li>
</ul>
</li>
<li><p><strong>Outcome:</strong> This inference directly points us to a defect in the code. The test's failure, given its trusted nature, <em>reveals</em> that the actual state of the code for this scenario is <strong>P is False</strong>. This effectively places the scenario in <strong>Row 4 (P False, Q False)</strong> of our truth table, confirming the presence of a bug that needs fixing. This is typical in <strong>regression testing</strong>, where a previously correct feature suddenly breaks.</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<h3 id="heading-scenario-2-validatingrefining-the-specification-falsifying-pq-or-confirming-known-incorrectness">Scenario 2: Validating/Refining the Specification (Falsifying P⟹Q or Confirming Known Incorrectness)</h3>
<p>This scenario arises when a test fails, and our primary focus is not immediately on debugging the code as if it's a regression. Instead, it's on understanding <em>why</em> the P⟹Q relationship (our hypothesis for this specific behavior) isn't holding, or simply confirming an expected failure. This can involve questioning the test itself, the underlying requirements, or confirming a deliberately incorrect state of the code.</p>
<ol>
<li><p><strong>Our Hypothesis (Being Challenged or Confirmed):</strong> We are either actively evaluating the validity of the implication "P⟹Q" for a specific behavior, or we are running a test against code we know is incomplete or incorrect.</p>
</li>
<li><p><strong>Test Execution and Observation:</strong> We run the test with its defined preconditions and inputs.</p>
</li>
<li><p><strong>If the Test Fails (Q is False):</strong> The interpretation here depends on our prior knowledge or intent about the code's state (P):</p>
<ul>
<li><p><strong>Sub-scenario 2A: Falsifying P⟹Q and Rethinking Specification (Corresponds to Row 2: P True, Q False):</strong></p>
<ul>
<li><p>We observe Q is False (the test fails).</p>
</li>
<li><p>If we then examine the code and the requirements, and we conclude that the code <em>should</em> have been correct for this scenario (meaning, our expectation/belief was P is True), then the test result means <strong>the specific instance of our hypothesis "P⟹Q" is FALSE.</strong></p>
</li>
<li><p>This direct falsification reveals a contradiction. We must then investigate:</p>
<ul>
<li><p>Is our initial belief that P was True mistaken (that is, is there a genuine bug in the code that makes P actually False, moving this to a Row 4 scenario)?</p>
</li>
<li><p>Or, is the test itself incorrect (its inputs or expected output are wrong), meaning our P⟹Q premise needs to be re-evaluated and corrected?</p>
</li>
<li><p>Or, have the underlying requirements changed or been misunderstood?</p>
</li>
</ul>
</li>
<li><p><strong>Outcome:</strong> This critical outcome prompts us to "rethink" – either the code needs fixing, or the test needs adjusting, or the specification needs clarification. This is common in <strong>exploratory testing</strong> or when working with new/evolving features where the exact behavior is still being defined.</p>
</li>
</ul>
</li>
<li><p><strong>Sub-scenario 2B: Confirming Known Incorrectness (Corresponds to Row 4: P False, Q False):</strong></p>
<ul>
<li><p>We observe Q is False (the test fails).</p>
</li>
<li><p>We <em>already know or intentionally designed</em> the code to be incorrect for this scenario (that is, we are actively developing a feature and haven't written the full code yet, or we're running a test against a known, un-fixed bug, so our expectation is P is False).</p>
</li>
<li><p>The test result simply <strong>confirms our prior knowledge that P is False</strong>. The test correctly highlights the missing or incorrect behavior. In this case, the P⟹Q implication is vacuously true, and the test effectively served its purpose of showing the existing defect.</p>
</li>
<li><p><strong>Outcome:</strong> This is typical in Test-Driven Development (TDD) in the Red phase, where a failing test for a not-yet-implemented feature confirms the "P is False" state, guiding development to make P True. It also applies when verifying that a bug fix indeed works: the test initially fails (confirming the bug), and then passes after the fix (confirming P is now True).</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749063701013/bc574591-90ec-4439-9b47-f0737d5a5384.jpeg" alt="girl looking into microscope" class="image--center mx-auto" width="4480" height="6720" loading="lazy"></p>
<h2 id="heading-a-closer-look-at-testing">A Closer Look at Testing</h2>
<h3 id="heading-the-illusion-of-correctness-affirming-the-consequent">The Illusion of Correctness: Affirming the Consequent</h3>
<p>Consider a common scenario where a test passes, seemingly validating our code:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_user_role</span>(<span class="hljs-params">user_id</span>):</span>
    <span class="hljs-keyword">if</span> user_id == <span class="hljs-number">42</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"admin"</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"guest"</span>

<span class="hljs-comment"># test</span>
<span class="hljs-keyword">assert</span> get_user_role(<span class="hljs-number">42</span>) == <span class="hljs-string">"admin"</span>
</code></pre>
<p>Here, our implicit claim (the specification) is: <strong>If the code is correct (P), then the output will match the expectation (Q).</strong></p>
<p>In this example, the test passes – the output is "admin" <strong>(Q)</strong>, but can we definitively conclude that the function is correct <strong>(P)</strong>? Not necessarily.</p>
<p>This scenario often exemplifies the logical fallacy of <strong>affirming the consequent</strong>. We see the desired outcome (Q) and mistakenly assume that our specific intended cause (P, the correctness of <em>our specific implementation path</em>) was the reason.</p>
<p><strong>The Problem:</strong> What if the real condition for an "admin" role should be checking a database, but we have temporarily hardcoded the value for testing? The test would pass, but the correctness is illusory. If we see P as false because the code did not implement the behaviour from the full specification, this corresponds to Row 3 (P False, Q True: False Positive) in our truth table.</p>
<p>As I mentioned before, deliberately implementing ¬P works well if ¬Q is observed, but is not useful, or even erroneous, if Q is observed.</p>
<p>Even without hardcoding, the output might match by coincidence, or because of factors outside the direct logic we intended to test. This can happen due to:</p>
<ul>
<li><p><strong>Default behavior:</strong> A broader system default might produce the expected output.</p>
</li>
<li><p><strong>Caching:</strong> A previous successful operation might have cached the result, bypassing the actual logic.</p>
</li>
<li><p><strong>Fallback logic:</strong> An unintended fallback mechanism produces the correct output despite an error in the primary path.</p>
</li>
<li><p><strong>Test harness bugs:</strong> Flaws in the testing setup itself might obscure real issues.</p>
</li>
</ul>
<h3 id="heading-the-role-and-risks-of-test-doubles">The Role and Risks of Test Doubles</h3>
<p>The challenges highlighted above are particularly relevant when using <strong>test doubles</strong>, such as Stubs and Mocks. These are artificial components that replace real dependencies (for example, databases, external APIs, time-sensitive operations) during testing.</p>
<ul>
<li><p><strong>Stubs</strong> focus on <strong>state</strong>: they provide pre-programmed fake data or return values to get the rest of the code under test working predictably, like the <code>get_user_role</code> example</p>
</li>
<li><p><strong>Mocks</strong> focus on <strong>behavior</strong>: they allow you to verify interactions, such as the number of calls made to a certain API, or how control flow flows through specific parts of the system.</p>
</li>
</ul>
<p>Both remove external dependencies, allowing you to isolate and focus on the internal logic of the code without noise or side effects. But using them without understanding their limitations can lead to <strong>false confidence</strong>.</p>
<p>If a test double simulates a "correct" response, but the real dependency it replaces has a bug, or the way the main code interacts with that dependency is flawed, the test will pass (Q is True) – yet P (the code's overall correctness in a real environment) might be False, leading to a dangerous false positive.</p>
<p>Whether you encounter such logical fallacies in your testing depends on precisely what behavior or state you are attempting to verify, and whether you are over-interpreting the test results.</p>
<h3 id="heading-test-scope-and-interpretation">Test Scope and Interpretation</h3>
<p>The choice of testing scope – from narrowly focused unit tests to broader integration tests, system tests, user acceptance tests (UAT), and even testing in production – represents a continuum. On this spectrum, various trade-offs are involved, especially concerning the effort-reward ratio. This effort is influenced by factors like individual developer skill, company engineering practices (for example, responsibility split between feature developer and dedicated tester roles), and industry regulations.</p>
<p>Generally:</p>
<ul>
<li><p><strong>Smaller-scoped tests</strong> (for example, unit tests) have fewer assumptions baked in and a shorter chain of logical implications. This translates to less risk of committing fallacies in both test implementation and test result interpretation. They are excellent for quickly verifying isolated units of code.</p>
</li>
<li><p><strong>Larger-scoped tests</strong> (for example, end-to-end integration tests) incorporate more real-world complexities and dependencies. While providing higher confidence in the system's overall behavior, they inherently increase the potential for confounding factors that can lead to false positives or make debugging more challenging.</p>
</li>
</ul>
<p>Being acutely aware of the assumptions implicit in each test, at every scope level, is paramount. Passing tests for the wrong reasons will inevitably cause problems down the road.</p>
<h3 id="heading-debugging-observability-and-mental-models">Debugging, Observability, and Mental Models</h3>
<p>Failing tests are not failures of the testing process but are, in fact, incredibly valuable learning moments. They represent opportunities to:</p>
<ul>
<li><p>Run focused debugging experiments to pinpoint the exact cause of the failure.</p>
</li>
<li><p>Refine your <strong>mental model of the code-to-outcome (P⟹Q) link</strong>. A failing test (where Q is False) tells you that your current understanding of P, or of the P⟹Q relationship, is flawed. Use this feedback to update your understanding of the code's actual behavior.</p>
</li>
<li><p>Improve both the code and the tests themselves.</p>
</li>
</ul>
<p>Enhance system <strong>observability</strong> to better detect and confirm outcomes (Q). The more clearly, from multiple angles, and through diverse methods we can observe Q (for example, logs, metrics, tracing, output inspection), the more confident we can be in its causes and, by extension, the actual state of P.</p>
<p>Crucially, avoid blindly fixing tests just to make them pass. Always ensure you thoroughly understand why a test failed and update your P⟹Q model accordingly. The ultimate goal is not just to fix current bugs, but to prevent them in the future by continually strengthening both the correctness of the code and the verifiability of its behavior.</p>
<h3 id="heading-falsifiable-tests-reveal-regressions">Falsifiable Tests Reveal Regressions</h3>
<p>Beyond avoiding false positives (where the code is incorrect but the test passes), a good test must also be <strong>falsifiable</strong>. This means the test must be genuinely capable of failing under certain (incorrect) conditions. An unfalsifiable test is a broken test – it cannot serve its purpose of revealing regressions or confirming the presence of bugs.</p>
<p>While we strive for the implication P⟹Q to hold true for all the scenarios we care about, it may not be true for all cases due to unforeseen or mistaken assumptions, or simply because the code is incorrect. The test's ability to demonstrate this incorrectness by failing under specific, well-defined conditions makes it profoundly valuable.</p>
<p>Some common culprits for unfalsifiable or "bad" tests include:</p>
<ul>
<li><p><strong>Vague or Untestable Specifications:</strong> Statements like "The system should behave well under most conditions," "It shouldn't crash randomly," or "The algorithm is robust" lack clear, measurable criteria. It's impossible to design a test that definitively passes or fails against such statements, thus rendering them effectively unfalsifiable.</p>
</li>
<li><p><strong>Broken Implementations of the Test Suite:</strong> The test code itself might be flawed, perhaps due to logical errors or control flow issues that prevent assertions from ever being reached or correctly evaluated, inadvertently taking the same passing path regardless of the code under test.</p>
</li>
<li><p><strong>Insufficient Test Data or Edge Cases:</strong> If tests only cover "happy path" scenarios and fail to include challenging inputs or boundary conditions, they might pass for incorrect code that only breaks under specific, untested circumstances.</p>
</li>
</ul>
<p>A robust specification clearly defines what constitutes success and failure. Correspondingly, a good test suite correctly implements that specification, making its tests both accurate and truly falsifiable.</p>
<h3 id="heading-take-a-step-back">Take a step back</h3>
<p>Critical thinkers might observe that the application of the four fundamental logical argument forms to coding scenarios, as initially presented, could be misleading in the complexities of real-world software.</p>
<p>The next section shows some nuances that arise when we transition from the clear-cut rules of formal logic to the often messy reality of software development.</p>
<p>Specifically:</p>
<ul>
<li><p>The first two points below show why the seemingly valid arguments of Modus Ponens and Modus Tollens may not always lead to reliable conclusions when applied to coding scenarios.</p>
</li>
<li><p>The last two points below show why the two common logical fallacies, Affirming the Consequent and Denying the Antecedent, may actually provide correct insights under specific real-world coding conditions.</p>
</li>
</ul>
<h2 id="heading-revisiting-the-four-statements-for-coding">Revisiting the Four Statements for Coding</h2>
<p>Here are the four arguments and their associated coding examples:</p>
<ol>
<li><p><strong>Modus Ponens:</strong> If you provide invalid input data (P), the code will show an error (Q).</p>
</li>
<li><p><strong>Modus Tollens:</strong> There are no error messages (¬Q), so the input data is valid (¬P).</p>
</li>
<li><p><strong>Affirming the Consequent (Fallacy):</strong> The code showed an error (Q), so you provided invalid data (P).</p>
</li>
<li><p><strong>Denying the Antecedent (Fallacy):</strong> You provided valid data (¬P), so you have no error (¬Q).</p>
</li>
</ol>
<p>Now, let's dive into the nuances of each:</p>
<h3 id="heading-modus-ponens">Modus Ponens</h3>
<ul>
<li><p><strong>Our coding example:</strong> If you provide invalid input data (P), then the code will show an error (Q).</p>
</li>
<li><p><strong>Why it may not always hold:</strong> This application of Modus Ponens assumes that either your code or any third-party code it relies upon will <em>always</em> properly detect and explicitly raise exceptions or show errors on bad data. In reality, systems might automatically fix or sanitize bad input, silence errors, or simply proceed with unexpected behavior without explicitly signaling an error, leading to a passing (or non-failing) state (¬Q) even when P (invalid input) was true.</p>
</li>
</ul>
<h3 id="heading-modus-tollens">Modus Tollens</h3>
<ul>
<li><p><strong>Our coding example:</strong> There are no error messages (¬Q), so the input data is valid (¬P).</p>
</li>
<li><p><strong>Why it may not always hold:</strong> This application of Modus Tollens assumes there are no automatic mechanisms within the system to fix or silence bad input <em>before</em> errors are typically displayed. If such "silent correction" or "error suppression" occurs, you might observe no error messages (¬Q), but the input data could still be invalid (P), rendering the conclusion (¬P) false despite the premise (¬Q) being true. This highlights the dangers of incomplete observability.</p>
</li>
</ul>
<h3 id="heading-affirming-the-consequent-fallacy-1">Affirming the Consequent (Fallacy)</h3>
<ul>
<li><p><strong>Our coding example:</strong> The code showed an error (Q), so you provided invalid data (P).</p>
</li>
<li><p><strong>Why it may actually be correct:</strong> While logically a fallacy, in specific, highly constrained real-world conditions, this inference can gain practical validity. If the error message is so uniquely and specifically defined that it can <em>only</em> be caused by invalid input data (P) and no other known factor, then this statement can become reliable. This is rare and typically requires meticulous error handling design where each error message maps unambiguously to a single root cause.</p>
</li>
</ul>
<h3 id="heading-denying-the-antecedent-fallacy-1">Denying the Antecedent (Fallacy)</h3>
<ul>
<li><p><strong>Our coding example:</strong> You provided valid data (¬P), so you have no error (¬Q).</p>
</li>
<li><p><strong>Why it may actually be correct:</strong> Although a fallacy in general logic, this inference can hold a high degree of practical confidence under certain programming paradigms (<strong>Functional Programming</strong>). If the code is sufficiently simple, purely functional (meaning outputs depend <em>only</em> on inputs and have no side effects), and has no external dependencies (like network or database interactions), then the absence of invalid data (¬P) can indeed make us reasonably confident that there will be no errors (¬Q). The lack of external variables and internal state makes the code's behavior highly predictable and directly tied to its inputs.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749061917858/db44dba5-2184-427a-8e28-27fc59904c49.jpeg" alt="dog with head tilted" class="image--center mx-auto" width="2778" height="4269" loading="lazy"></p>
<p>You may now be thinking: what’s the point of studying logic if it has so many loopholes and edge cases when applied to coding?</p>
<h2 id="heading-the-missing-ingredient-if-and-only-if">The Missing Ingredient – If and Only If</h2>
<p>In our exploration of logical implications, we've focused primarily on the <strong>unidirectional relationship</strong> P⟹Q ("If P, then Q"). This statement tells us what happens <em>if</em> P is true, but it remains silent on whether Q <em>only</em> happens when P is true. It's like saying, "If it rains, the ground gets wet." This is true, but the ground can also get wet if a sprinkler is on, even if it's not raining.</p>
<p>But in many critical contexts, especially in rigorous scientific theories and robust software systems, we often seek a much stronger relationship: one where the truth of Q absolutely <em>depends</em> on the truth of P, and vice versa. This powerful <strong>bidirectional relationship</strong> is captured by the phrase "<strong>If and Only If</strong>" (P⟺Q).</p>
<h3 id="heading-what-if-and-only-if-means-a-stronger-statement">What "If and Only If" Means: A Stronger Statement</h3>
<p>When we assert "P⟺Q", we're making two distinct claims simultaneously:</p>
<ol>
<li><p><strong>If P, then Q</strong> (P⟹Q): P is a sufficient condition for Q. Whenever P is true, Q must also be true.</p>
</li>
<li><p><strong>If Q, then P</strong> (Q⟹P): P is also a necessary condition for Q. Whenever Q is true, P must also be true. In other words, Q cannot be true without P being true.</p>
</li>
</ol>
<p>Notice the <strong>significant increase in the strength</strong> of the statement. "If P, then Q" merely states a consequence. "P⟺Q" declares a <strong>definitive equivalence</strong>, where P and Q are inextricably linked. They rise and fall together – one cannot be true without the other being true, and one cannot be false without the other being false.</p>
<h3 id="heading-bidirectional-truth-table-unambiguous-relationships">Bidirectional Truth Table: Unambiguous Relationships</h3>
<p>Let's construct the truth table for P⟺Q to clearly see this strong relationship.</p>
<p>P⟺Q is logically equivalent to (P⟹Q)∧(Q⟹P).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747678444501/8d498249-eec2-46ca-a5c1-85801eb1b350.png" alt="Truth table with columns P, Q, P->Q, Q->P, P<->Q" class="image--center mx-auto" width="1226" height="323" loading="lazy"></p>
<h4 id="heading-creating-the-table-columns-4-and-5-are-new">Creating the Table (columns 4 and 5 are new):</h4>
<ul>
<li><p><strong>Q⟹P (Column 4):</strong> We apply the standard implication rules, but with Q as our "if" and P as our "then." For instance, in Row 3, Q is True and P is False, so Q⟹P is False.</p>
</li>
<li><p><strong>P⟺Q (Column 5):</strong> This is the logical <strong>AND</strong> of the P⟹Q and Q⟹P columns. For P⟺Q to be True, both component implications must be True, which explains why you see less Trues in the bidirectional implication compared to any of the unidirectional implications.</p>
</li>
</ul>
<h3 id="heading-implications-for-the-two-common-fallacies">Implications for the Two Common Fallacies</h3>
<p>The clarity provided by "If and Only If" is particularly powerful in preventing the very logical fallacies we discussed earlier: Affirming the Consequent and Denying the Antecedent. These fallacies arise from the incorrect assumption that an "if-then" statement implies an "if and only if" relationship.</p>
<p>Let's revisit them with the lens of <strong>P⟺Q If and Only If you provided invalid data (P), then the code will show an error (Q)</strong>:</p>
<h4 id="heading-affirming-the-consequent-no-more-ambiguity">Affirming the Consequent: No More Ambiguity</h4>
<ul>
<li><p><strong>The Fallacy (assuming unidirectional P⟹Q):</strong></p>
<ul>
<li><p>If the code showed an error (Q), then you provided invalid data (P).</p>
</li>
<li><p>Previously, when P⟹Q was True and Q was True, P could be True (Row 1) or False (Row 3). This ambiguity led to the fallacy.</p>
</li>
</ul>
</li>
<li><p><strong>With P⟺Q:</strong></p>
<ul>
<li><p>Now, look at the P⟺Q column in the table. When P⟺Q is True and Q is True (Row 1), P is <strong>unambiguously True</strong>. The confusion from Row 3 is gone because if Q were True while P was False, P⟺Q would be False (as Q⟹P would be False), thus making that row irrelevant for valid modus ponens inference under the P⟺Q premise.</p>
</li>
<li><p>In a system designed with P⟺Q in mind, knowing that Q is True (observing an error) would <strong>force</strong> the conclusion that P is True (invalid data is the cause), assuming the "if and only if" relationship holds true for that specific system design.</p>
</li>
</ul>
</li>
</ul>
<h4 id="heading-denying-the-antecedent-unmistakable-consequences">Denying the Antecedent: Unmistakable Consequences</h4>
<ul>
<li><p><strong>The Fallacy (assuming unidirectional P⟹Q):</strong></p>
<ul>
<li><p>You provided valid data (¬P), so you have no error (¬Q).</p>
</li>
<li><p>Previously, when P⟹Q was True and P was False, Q could be True (Row 3) or False (Row 4). This ambiguity led to the fallacy.</p>
</li>
</ul>
</li>
<li><p><strong>With P⟺Q:</strong></p>
<ul>
<li><p>Now, when P⟺Q is True and P is False (Row 4), Q is <strong>unambiguously False</strong>. The problematic scenario from Row 3 (where P was False but Q was True) is irrelevant here because P⟺Q would be False in that case (specifically, Q⟹P would be False).</p>
</li>
<li><p>If your system genuinely adheres to "P⟺Q", then knowing that P is False (valid data provided) <strong>guarantees</strong> that Q is False (no error messages).</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-practical-mitigation-in-coding">Practical Mitigation in Coding</h3>
<p>The insights from "If and Only If" are more than just theoretical. Practically, both fallacies (Affirming the Consequent and Denying the Antecedent) can be mitigated by striving for conditions that approximate an "if and only if" relationship in your code and tests.</p>
<h4 id="heading-focused-unit-tests">Focused Unit Tests</h4>
<p>Design unit tests that are so granular and isolated that they effectively aim to establish an "if and only if" scenario for a tiny piece of logic. By thoroughly mocking or controlling all external dependencies and environmental factors, you reduce the impact of "other causes."</p>
<p>If your test for a specific input passes, you want to be as confident as possible that it passed <em>only</em> because the code handled that specific input correctly, and not due to some irrelevant side effect. Similarly, if it fails, you want to be sure that the failure points directly to the intended logical path.</p>
<h4 id="heading-exception-handling-and-specificity">Exception Handling and Specificity</h4>
<p>Instead of catching broad <code>Exception</code> types, catch and handle specific exceptions. This helps differentiate between various "causes" (P1​,P2​,…) that might lead to a generic "error" (Q). The more precise your error handling, the closer you get to a scenario where "If X error, then Y specific cause," moving towards a bidirectional understanding of error conditions.</p>
<h4 id="heading-test-driven-development-tdd-and-mutation-testing">Test-Driven Development (TDD) and Mutation Testing</h4>
<p>These methodologies inherently push towards P⟺Q thinking. TDD encourages writing a failing test <em>first</em> (¬Q), which <em>then</em> necessitates a specific code change (P) to make it pass.</p>
<p>Mutation testing, which we'll explore further, takes this a step further by ensuring that your tests are robust enough to <em>fail</em> when code is subtly altered (that is, proving that ¬P leads to ¬Q, and thus, that the original P was indeed necessary for Q).</p>
<p>By consciously aiming for "if and only if" relationships in your code's design and your testing strategies, you can build systems that are not only predictable but also much easier to debug and reason about, moving beyond mere correlation to a deeper understanding of cause and effect.</p>
<h3 id="heading-callback-to-mutation-testing">Callback to Mutation Testing</h3>
<p>In the earlier section on <strong>Assigning Real-World Meanings to Logic</strong>, we discussed:</p>
<blockquote>
<p>When testing the <strong>happy path</strong>, we are verifying that the implication <strong>P</strong>⟹<strong>Q (If input is good, then no error)</strong> holds true.</p>
<p>When testing the <strong>unhappy path (mutation testing)</strong>, we are verifying that <strong>¬P</strong>⟹<strong>¬Q (If input is not good, then an error occurs)</strong> holds true.</p>
</blockquote>
<p>This dual view is key to understanding how mutation testing contributes to software correctness.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749063165908/e1e3736c-75dd-4f1f-81bb-fd7d4f4f7837.jpeg" alt="artistic representation of molecular structures" class="image--center mx-auto" width="4000" height="4000" loading="lazy"></p>
<h2 id="heading-mutation-testing-testing-the-tests">Mutation Testing: Testing the Tests</h2>
<p>Mutation testing deliberately introduces small faults (mutations) in the code and checks whether the test suite detects them by failing. This process assesses not the <em>code</em>, but the <em>tests themselves</em>.</p>
<p>In a robust test suite, we strive for two ideal conditions:</p>
<ul>
<li><p>All <strong>correct</strong> implementations should <strong>pass</strong> the tests.</p>
</li>
<li><p>All <strong>incorrect</strong> implementations should <strong>fail</strong> the tests.</p>
</li>
</ul>
<p>If a mutated (wrong) version of the code is introduced and causes no test failures, that defeats the fundamental purpose of testing. It means your tests aren't sensitive enough to catch a deviation from correctness. Mutations reveal hidden assumptions or gaps in your test coverage, acting as a sensitivity probe for your test suite.</p>
<p><strong>Example code mutations:</strong></p>
<ul>
<li><p>Changing an arithmetic operator (<code>+</code> to <code>-</code>, <code>&gt;</code> to <code>&gt;=</code>).</p>
</li>
<li><p>Flipping a boolean condition (<code>true</code> to <code>false</code>).</p>
</li>
<li><p>Deleting or duplicating a statement.</p>
</li>
<li><p>Modifying a constant value.</p>
</li>
</ul>
<p><strong>Common Python mutation testing tools:</strong></p>
<ul>
<li><p><strong>mutmut</strong> uses Python’s built-in <code>ast</code> module.</p>
</li>
<li><p><strong>cosmic-ray</strong> uses <code>parso</code>, which provides a more complete AST.</p>
</li>
</ul>
<p>These tools rely on abstract syntax trees to surgically mutate code.</p>
<p>You can even swap out underlying AST libraries for different precision or completeness: <a target="_blank" href="https://github.com/boxed/mutmut/issues/281">https://github.com/boxed/mutmut/issues/281</a></p>
<h3 id="heading-logic-behind-mutation-testing">Logic Behind Mutation Testing</h3>
<p>Let's formalize the logical mapping of mutation testing, recalling our definitions:</p>
<ul>
<li><p>Let P: Code is correct.</p>
</li>
<li><p>Let Q: Tests pass.</p>
</li>
</ul>
<p>Standard <strong>happy path testing</strong> primarily checks that P⟹Q – "if the code is correct, then tests pass."</p>
<p><strong>Mutation testing</strong> focuses on the other side of the coin: we intentionally make ¬P true (by introducing a fault), and then we expect ¬Q (the tests should fail). This process rigorously checks whether the implication ¬P⟹¬Q ("if the code is <em>not</em> correct, then the tests <em>fail</em>") holds true for your test suite.</p>
<p>But there's a deeper, more powerful logical implication here:</p>
<p>As we learned earlier, the statement ¬P⟹¬Q is <strong>logically equivalent</strong> to its <strong>contrapositive</strong>, Q⟹P.</p>
<p>So, by successfully verifying that introducing a fault (¬P) leads to a test failure (¬Q), we are simultaneously validating the contrapositive: <code>if tests pass (Q), then the code must be correct (P)</code>.</p>
<p>This is incredibly significant! It moves us much closer to establishing a <strong>bidirectional guarantee</strong> between our code and our tests: P⟺Q (code correctness is tightly coupled with test success). Mutation testing helps us confidently eliminate false positives in the test suite – situations where Q is true (the test passes) but P is false (the code is actually incorrect).</p>
<p>In a world where LLMs help us write and refactor code quickly, having this "if and only if" confidence in our test suite is invaluable for ensuring the generated or refactored code truly meets expectations.</p>
<h3 id="heading-clarifying-the-kinds-of-failures"><strong>Clarifying the Kinds of Failures</strong></h3>
<p>In software, we typically categorize errors into three main types:</p>
<ul>
<li><p><strong>Syntax errors:</strong> Violations of the language's grammatical rules (for example, missing colon, invalid keyword). These prevent the code from running at all.</p>
</li>
<li><p><strong>Runtime errors:</strong> Errors that occur during program execution, often due to unexpected conditions (for example, <code>TypeError</code>, <code>AttributeError</code>, <code>ZeroDivisionError</code>).</p>
</li>
<li><p><strong>Logic errors:</strong> The program runs without crashing, but it produces an incorrect result or behaves in a way that doesn't match the intended specification (for example, wrong algorithm, wrong return value).</p>
</li>
</ul>
<p>Mutation testing focuses on <strong>logic errors</strong> – failures where the program runs, but produces incorrect results. These are usually caught via <code>AssertionError</code> in the "Assert" phase of the Arrange–Act–Assert (AAA) testing pattern.</p>
<p>You could argue pedantically that <code>AssertionError</code> is a runtime error, but in testing, we treat it as a <strong>signal for logical failure</strong>:</p>
<blockquote>
<p><em>"The function ran, but the output didn’t match the expected behavior."</em></p>
</blockquote>
<p>Mutation testing assumes that syntax and runtime errors are already handled. Its purpose is to validate whether the test suite reliably catches logical misbehavior.</p>
<h3 id="heading-a-deeper-falsification-perspective">A Deeper Falsification Perspective</h3>
<p>Now, let's connect mutation testing back to <strong>Karl Popper's principle of falsification</strong>, which we introduced earlier in the context of scientific reasoning. Recall that Popper argued scientific theories gain strength not by being "proven," but by <em>surviving rigorous attempts to disprove them</em>. The core idea of falsification logic is that to disprove an implication like P⟹Q, you only need to find one instance where P is True and Q is False.</p>
<p>Mutation testing applies this same powerful principle, but to our test suite's effectiveness:</p>
<p>Instead of trying to <em>prove</em> directly that our tests are perfect, mutation testing takes a falsification approach to the implication <strong>¬P⟹¬Q ("If the code is incorrect, then the tests fail").</strong> It actively tries to <strong>falsify</strong> this crucial relationship.</p>
<p>If we introduce a mutation (making ¬P true, that is, the code is now incorrect) but the existing test suite <em>still passes</em> (meaning Q is true), then we have found an instance where:</p>
<ol>
<li><p>¬P is True (the code is incorrect due to the mutation).</p>
</li>
<li><p>Q is True (the test still passes).</p>
</li>
</ol>
<p>In this scenario, the implication <strong>¬P⟹¬Q is falsified</strong> because we have a True antecedent (¬P) leading to a False consequent (¬Q is false, because Q is true).</p>
<p>And, critically, if ¬P⟹¬Q is falsified, then its logically equivalent contrapositive, Q⟹P ("If the tests pass, then the code is correct"), is <em>also</em> falsified. This means we can no longer trust that a passing test suite reliably indicates correct code. Our desired P⟺Q relationship is broken – <strong>the test suite is no longer fully effective</strong> at guaranteeing correctness.</p>
<p>By pushing for zero surviving mutants, mutation testing forces us to minimize the surface area of these "hidden assumptions" in our test suite. It demands highly sensitive and specific tests that can pinpoint even subtle logical flaws, thereby moving us closer to building truly resilient systems.</p>
<h3 id="heading-comparing-tdd-red-phase-and-mutation-testing">Comparing TDD (Red Phase) and Mutation Testing</h3>
<p>Both methodologies, albeit through different means and at different stages of the development cycle, aim to establish confidence in the <strong>¬P ⟹ ¬Q</strong> relationship.</p>
<p><strong>Key Differences Summarized:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>TDD (Red Phase)</td><td>Mutation Testing</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Primary Goal</strong></td><td>Drive new code development. Confirm a bug/feature.</td><td>Evaluate the quality/completeness of existing tests.</td></tr>
<tr>
<td><strong>Code State</strong></td><td>Production code is incomplete or buggy.</td><td>Production code is (assumed to be) correct.</td></tr>
<tr>
<td><strong>Test State</strong></td><td>The <em>new</em> test is expected to fail.</td><td><em>Existing</em> tests are expected to fail (due to mutants).</td></tr>
<tr>
<td><strong>Initiator</strong></td><td>Developer wanting to add functionality/fix bug.</td><td>Tool that inserts artificial bugs into code.</td></tr>
<tr>
<td><strong>"Bugs"</strong></td><td>Actual, intended bugs or missing features.</td><td>Artificial, subtle changes to the code.</td></tr>
</tbody>
</table>
</div><h2 id="heading-toward-if-and-only-if-confidence">Toward If-and-Only-If Confidence</h2>
<p>Ultimately, the goal in software development is to establish if-and-only-if relationships whenever possible, both in the code implementation and especially in the sensitivity of the test suite to the code under test.</p>
<p>This means <strong>if a certain condition (P) is true, then a specific outcome (Q) <em>must</em> occur, and if Q occurs, then P <em>must</em> have been the cause</strong>. Achieving this level of clarity comes from:</p>
<ul>
<li><p>A deep understanding of the problem.</p>
</li>
<li><p>Aligned expectations during requirements gathering.</p>
</li>
<li><p>Logical analysis and interpretation of well-designed experiments.</p>
</li>
<li><p>Adherence to Single Responsibility Principle in SOLID</p>
</li>
<li><p>Rigorous tests with meaningful coverage.</p>
</li>
</ul>
<p>This allows us to understand how <strong>control flow</strong> and <strong>data flow</strong> work with greater depth and confidence, leading to better inferences throughout the entire software development lifecycle.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749062596293/9bfb566a-5e3c-4fec-ac42-326aa22532c8.jpeg" alt="Monarch Butterfly resting on butterfly bush flower" class="image--center mx-auto" width="4212" height="2812" loading="lazy"></p>
<h2 id="heading-real-world-challenges">Real-World Challenges</h2>
<p>While striving for perfect "if-and-only-if" relationships provides a powerful logical ideal, the messy reality of modern software development presents significant hurdles. The very characteristics that make large systems powerful and scalable – their intricate interconnections and inherent dynamism – simultaneously obscure clear cause-and-effect relationships, making precise logical reasoning and debugging an ongoing battle.</p>
<h3 id="heading-a-web-of-complexity">A Web of Complexity</h3>
<h4 id="heading-fan-in-fan-out-the-nature-of-modern-systems">Fan-In, Fan-Out: The Nature of Modern Systems</h4>
<p>Any reasonably large software system rarely operates through purely linear control and data flows. Fan-out and fan-in patterns – where many components are called and then their results merged – are inevitable.</p>
<p>For example:</p>
<ul>
<li><p>In <strong>ETL pipelines</strong>, data may be ingested from multiple sources (external APIs, CSVs) and logged to multiple destinations (files, databases).</p>
</li>
<li><p>In <strong>concurrent programming</strong>, Python’s <code>ProcessPoolExecutor</code> splits data into chunks processed in parallel, then recombines the results.</p>
</li>
</ul>
<h4 id="heading-srp-meets-real-world-boundaries">SRP Meets Real-World Boundaries</h4>
<p>Just as functional programming must eventually perform I/O, the <strong>Single Responsibility Principle (SRP)</strong> runs into real-world boundaries, whether conceptual or infrastructural. At some point, something must glue these isolated units together.</p>
<p>Orchestration logic might live in a single function, span multiple files, or even distribute across microservices and machines communicating over networks. While this decomposition enhances modularity, it also increases surface area for bugs involving:</p>
<ul>
<li><p><strong>Side effects:</strong> Unintended changes to system state outside a component's explicit outputs.</p>
</li>
<li><p><strong>Circular dependencies:</strong> Components relying on each other in a loop, leading to difficult-to-trace behavior.</p>
</li>
<li><p><strong>Interface drift:</strong> Changes in one component's input/output expectations not being correctly reflected elsewhere.</p>
</li>
<li><p><strong>Race conditions:</strong> Timing-dependent bugs in concurrent operations.</p>
</li>
<li><p><strong>Serialization issues:</strong> Problems translating data between different formats or systems.</p>
</li>
<li><p><strong>Network unreliability:</strong> Unpredictable latency, packet loss, or disconnections in distributed systems.</p>
</li>
</ul>
<h4 id="heading-the-double-edged-sword-of-abstraction">The Double-Edged Sword of Abstraction</h4>
<p>This web of dependencies is the price of progress, made manageable only through better tooling and abstractions.</p>
<ul>
<li><p>If boundaries are <strong>well-designed, observable, and testable</strong>, they enable asynchronous collaboration, improve long-term maintainability, and increase developer confidence. (See GitHub Playbook in References)</p>
</li>
<li><p>If systems <strong>lack architectural coherence</strong> or fall behind evolving needs, they calcify into technical debt that demoralizes even the most motivated teams.</p>
</li>
</ul>
<h4 id="heading-clean-code-is-contextual">Clean Code Is Contextual</h4>
<p>While abstractions and orchestration help manage complexity, overusing design patterns or creating unnecessary class layers can introduce needless indirection. This is a common counterargument to architectural purism.</p>
<p>Ultimately, what counts as "clean code" is context-dependent. It varies with programmer skill, the tooling at hand (linters, tests, Copilot), and whether the project is a throwaway script or a multi-year infrastructure investment. Architectural practices like SRP should evolve alongside those constraints.</p>
<h3 id="heading-the-butterfly-effect-of-bugs">The Butterfly Effect of Bugs</h3>
<h4 id="heading-from-srp-to-reasoning-chains">From SRP to Reasoning Chains</h4>
<p>Previously, we focused on simple, direct cause-effect logic (P ⟹ Q), but real-world systems are messier.</p>
<p>The more we adhere to SRP through small, focused functions, the more we create longer chains of logic. This improves separation of concerns but also extends the reasoning required to debug behavior.</p>
<h4 id="heading-debugging-in-a-causal-fog">Debugging in a Causal Fog</h4>
<p>A seemingly minor trigger (O) can cascade through a chain like O⟹P⟹Q⟹R, which we may not fully understand due to knowledge silos, evolving requirements, or runtime dynamism.</p>
<p>Even when we understand the components, precisely identifying “P” is hard, much like how redefining a research question shifts the statistical population being studied. In complex systems with <strong>feedback loops</strong> (recommender engines), there might not be a single "root cause" at all.</p>
<h4 id="heading-short-term-triage-vs-long-term-insight">Short-Term Triage vs. Long-Term Insight</h4>
<p>Finding the true origin of a bug often demands experimentation, telemetry, and broad system insight. These investigations produce robust, future-proof fixes but take time.</p>
<p>In on-call scenarios, however, urgency reshapes priorities. Fast mitigations and clear communication often take precedence over deep diagnosis.</p>
<h3 id="heading-masked-by-design-and-debt">Masked by Design and Debt</h3>
<p>As systems scale, failure stops looking like a crash. Instead, it shows up as a retry spike, a slow metric drift, or silent fallback behavior.</p>
<p>Modern fault-tolerant systems, built with retries, failovers, circuit breakers, and autoscaling, are designed to recover quickly. This resilience often masks deeper problems, delaying detection for weeks and making root cause analysis harder.</p>
<p>Operating in <strong>non-deterministic environments</strong> with flaky networks, race conditions, or dynamic routing adds further ambiguity. Small symptoms become harder to link back to specific causes.</p>
<p>Compounding this, <strong>technical debt</strong> driven by weak technical leadership, shifting priorities or time pressure weakens the system’s observability and test coverage. Teams inherit brittle, poorly understood code, making it hard to draw clean lines between cause and effect.</p>
<p>Even the best engineers struggle in such conditions. When a system resists clarity, it doesn’t just block debugging. It erodes trust, slows learning, and fuels long-term burnout.</p>
<h2 id="heading-glimmers-of-hope-tools-and-practices-for-clarity">Glimmers of Hope: Tools and Practices for Clarity</h2>
<p>Despite these challenges, several strategies and practices offer a path toward more robust and understandable software.</p>
<h3 id="heading-leveraging-design-patterns">Leveraging Design Patterns</h3>
<p>Design patterns offer a shared vocabulary and time-tested strategies for structuring systems. When applied well, they tame complexity, reduce technical debt, and make behavior more predictable.</p>
<p>They also tend to concentrate similar failure modes. The same bug might appear across companies or industries, creating a wealth of prior art and solution playbooks. Familiarity with patterns can accelerate debugging and deepen shared understanding across teams.</p>
<h3 id="heading-nurturing-expert-mentorship">Nurturing Expert Mentorship</h3>
<p>Promoting mentors based on real technical impact instead of tenure builds stronger teams and avoids the <strong>Peter Principle</strong> (people in a hierarchy tend to rise to a level of respective incompetence).</p>
<p>Great mentors teach more than skills – they model falsifiability, independent thinking, and an ability to reason under uncertainty.</p>
<p>They help others challenge assumptions, navigate tradeoffs, and grow both technically and interpersonally. In systems where root causes are murky, this kind of leadership is essential.</p>
<p>One of the most powerful techniques that scales from mentorship to code is <strong>falsification</strong>: the disciplined search for counterexamples. Whether applied in design reviews, debugging sessions, or automated tests, this mindset anchors reasoning in reality.</p>
<h2 id="heading-the-power-of-falsification-in-testing">The Power of Falsification in Testing</h2>
<p>The deliberate search for counterexamples is core to building reliable systems.</p>
<ul>
<li><p>In algorithm design, testing edge cases is just falsification in disguise: finding where your logic breaks.</p>
</li>
<li><p>In code, <strong>fuzz testing</strong> (Atheris) throws diverse inputs at functions to expose falsifying examples.</p>
</li>
<li><p><strong>Property-based testing</strong> (Hypothesis) goes further by generating inputs that satisfy certain rules, then shrinks failures to their minimal form. This greatly improves reproducibility and helps stress-test concurrency issues.</p>
</li>
</ul>
<p>The more rigorously we attempt to falsify our assumptions, the more confidently we can reason about behavior using tools like Modus Ponens and Modus Tollens.</p>
<p>Assumptions are always present in software to simplify complexity. The question is whether they're <strong>explicitly codified in tests</strong> or <strong>left hidden and fragile</strong>.</p>
<p>Of course, no test is ever bulletproof: our assumptions could be mistaken, or the world could change. That’s why critical thinking, discerning "what should be" versus "what is", remains essential as newer generations increasingly rely on AI tools like Large Language Models.</p>
<p>This deliberate, <strong>falsification-driven approach</strong> is paramount for building reliable software. It underpins sophisticated testing techniques designed to expose hidden assumptions and break our logical chains.</p>
<p>While testing helps us uncover where our reasoning might falter, some domains demand an even higher degree of certainty. For those critical systems, we turn to the ultimate tools for logical rigor: <strong>Proof Assistants</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749062895395/f92ed2e7-f1fd-4351-a9d3-12c436c989f1.jpeg" alt="row of dominos" class="image--center mx-auto" width="5184" height="3888" loading="lazy"></p>
<h2 id="heading-proof-assistants">Proof Assistants</h2>
<p>While traditional testing and fuzzing are powerful for finding bugs, they fundamentally cannot guarantee correctness for all possible inputs or scenarios. They can only prove the <em>presence</em> of bugs, not their <em>absence</em>.</p>
<p>To achieve formal, mathematically verified proofs of program behavior – providing the strongest possible guarantees – we turn to <strong>proof assistants</strong>. These tools allow us to build step-by-step logical proofs, ensuring that a program or system design adheres to its specification with absolute rigor.</p>
<h3 id="heading-prolog"><strong>Prolog</strong></h3>
<p>Prolog offers a relatively straightforward entry point into the world of logic programming and theorem proving. <strong>SWI-Prolog</strong> is a common interpreter (a <strong>REPL</strong>, or Read-Eval-Print Loop) for Prolog.</p>
<p>You interact with Prolog by providing it with a knowledge base composed of <code>facts</code> and <code>rules</code> (which are a type of logical clause called <strong>Horn clauses</strong>). You then pose <code>queries</code>.</p>
<h4 id="heading-installing-swi-prolog">Installing SWI-Prolog</h4>
<p>You can download SWI-Prolog from its official website: <a target="_blank" href="https://www.swi-prolog.org/download/stable">https://www.swi-prolog.org/download/stable</a><br>Follow the instructions for your operating system (Windows, macOS, or Linux).</p>
<p>On Ubuntu/Debian, you can usually install it via:</p>
<pre><code class="lang-bash">sudo apt update
sudo apt install swi-prolog
</code></pre>
<h4 id="heading-using-prolog-repl-vs-file">Using Prolog: REPL vs. File</h4>
<ul>
<li><p><strong>REPL (</strong><code>swipl</code>) is best for: Quick, interactive tests of single facts or rules, and posing queries to an <em>already loaded</em> knowledge base.</p>
</li>
<li><p><strong>A File (</strong><code>.pl</code> extension) is best for: Defining your <strong>entire knowledge base</strong> (multiple facts and rules) and storing your program for reusability. This is the standard way to work with Prolog for anything beyond a few lines.</p>
</li>
</ul>
<h4 id="heading-example-a-simple-knowledge-base">Example: A Simple Knowledge Base</h4>
<p>Let's define a knowledge base to represent who has a job and who is a coding instructor.</p>
<p><strong>1. Create a file</strong> named <code>knowledge.pl</code> with the following content:</p>
<pre><code class="lang-haskell">% knowledge.pl
% <span class="hljs-type">This</span> file defines a small knowledge base <span class="hljs-keyword">in</span> <span class="hljs-type">Prolog</span>.
% <span class="hljs-type">In</span> <span class="hljs-type">Prolog</span>, all statements (facts and rules) about the same predicate
% (identified by its name <span class="hljs-type">AND</span> number <span class="hljs-keyword">of</span> arguments, e.g., 'has_job' with <span class="hljs-number">1</span> argument is 'has_job/<span class="hljs-number">1</span>')
% must be written consecutively without other predicate definitions <span class="hljs-keyword">in</span> between.

% <span class="hljs-comment">--- Definitions for the 'has_job' predicate (takes 1 argument) ---</span>

% <span class="hljs-type">Fact</span>: <span class="hljs-type">Alice</span> has a job.
<span class="hljs-title">has_job</span>(alice).

% <span class="hljs-type">Fact</span>: <span class="hljs-type">Bob</span> has a job.
<span class="hljs-title">has_job</span>(bob).

% <span class="hljs-type">Rule</span>: <span class="hljs-type">Anyone</span> (represented by variable <span class="hljs-type">X</span>) has a job <span class="hljs-type">IF</span> they are a coding instructor.
% ':-' means '<span class="hljs-keyword">if</span>'. '<span class="hljs-type">X'</span> is a variable (starts with uppercase).
<span class="hljs-title">has_job</span>(<span class="hljs-type">X</span>) :- is_coding_instructor(<span class="hljs-type">X</span>).

% <span class="hljs-comment">--- Definitions for the 'is_coding_instructor' predicate (takes 1 argument) ---</span>

% <span class="hljs-type">Fact</span>: <span class="hljs-type">Alice</span> is a coding instructor.
<span class="hljs-title">is_coding_instructor</span>(alice).
</code></pre>
<p><strong>What each line does:</strong></p>
<ul>
<li><p>Lines starting with <code>%</code>: These are comments for human readability, ignored by Prolog. They explain the file's purpose and key rules like predicate grouping.</p>
</li>
<li><p><code>has_job(alice).</code> / <code>has_job(bob).</code>: These are facts. They assert simple truths, like "Alice has a job." The <code>.</code> at the end is mandatory for every statement.</p>
</li>
<li><p><code>has_job(X) :- is_coding_instructor(X).</code>: This is a rule. It states a conditional truth: "For any <code>X</code>, <code>X</code> has a job <em>if</em> <code>X</code> is a coding instructor." <code>X</code> is a variable (always starts with an uppercase letter), and <code>:-</code> means "if." This rule allows Prolog to deduce new information.</p>
</li>
<li><p><code>is_coding_instructor(alice).</code>: Another fact, asserting "Alice is a coding instructor." It's placed after all <code>has_job/1</code> clauses to satisfy Prolog's grouping rule.</p>
</li>
</ul>
<p><strong>2. Load and Query in the REPL:</strong></p>
<p>Open your terminal and type <code>swipl</code>. Once at the <code>?-</code> prompt, load the file and then pose your queries:</p>
<pre><code class="lang-bash">$ swipl
?- [knowledge].   % Load the <span class="hljs-string">'knowledge.pl'</span> file (omit .pl, use square brackets and a period)
% Press Enter. Prolog will confirm it loaded the file, e.g., <span class="hljs-string">'% knowledge.pl compiled...'</span>
True.

?- has_job(alice). % Query: Does Alice have a job?
% Press Enter. Prolog gives you a solution, <span class="hljs-keyword">then</span> waits.
True.              % Output: Yes, because it<span class="hljs-string">'s a fact.
% After '</span>True.<span class="hljs-string">', you'</span>ll see the <span class="hljs-string">'?- '</span> prompt again, indicating Prolog is ready <span class="hljs-keyword">for</span> your next query.
% If there were multiple ways to prove <span class="hljs-string">'True.'</span>, Prolog would present the first <span class="hljs-string">'True.'</span> <span class="hljs-keyword">then</span> <span class="hljs-built_in">wait</span> <span class="hljs-keyword">for</span> you to press <span class="hljs-string">';'</span> <span class="hljs-keyword">for</span> alternatives, <span class="hljs-keyword">then</span> Enter to confirm the final <span class="hljs-string">'True.'</span> or <span class="hljs-string">'False.'</span>.

?- has_job(carol). % Query: Does Carol have a job?
% Press Enter.
False.             % Output: No, Prolog cannot prove it from its knowledge.

?- has_job(X).     % Query: Who has a job? (Find values <span class="hljs-keyword">for</span> X)
% Press Enter
X = alice ;        % Prolog finds Alice as the first solution. Type <span class="hljs-string">';'</span> and press Enter to ask <span class="hljs-keyword">for</span> the next solution.
X = bob ;          % It finds Bob. Type <span class="hljs-string">';'</span> and press Enter <span class="hljs-keyword">for</span> the next solution.
X = alice          % It finds Alice again (this time deduced via the rule and is_coding_instructor(alice)).
% Press Enter. This accepts the current <span class="hljs-built_in">set</span> of solutions and stops searching <span class="hljs-keyword">for</span> more.
False.             % Output: Indicates no more solutions found after the last <span class="hljs-string">'Enter'</span> (or <span class="hljs-keyword">if</span> you explicitly chose not to search further).

?- halt.           % Type <span class="hljs-string">'halt.'</span> to <span class="hljs-built_in">exit</span> the Prolog REPL cleanly.
% Alternatively, you can often use Ctrl+D (press and hold Ctrl, <span class="hljs-keyword">then</span> D) to <span class="hljs-built_in">exit</span> most REPLs.
</code></pre>
<p><strong>The Prolog example clearly demonstrates:</strong></p>
<ul>
<li><p><strong>"Is P(X) true for a specific X?"</strong>: Shown by <code>?- has_job(alice).</code> (returns <code>True.</code>) and <code>?- has_job(carol).</code> (returns <code>False.</code>).</p>
</li>
<li><p><strong>"Is there an X for which P(X) is true?"</strong>: Shown by <code>?- has_job(X).</code> (provides solutions like <code>X = alice</code>, <code>X = bob</code>).</p>
</li>
</ul>
<h4 id="heading-prolog-limitations">Prolog Limitations</h4>
<p>Prolog's limitations become evident when attempting to reason about falsity or non-existence. <strong>You cannot directly ask "Is there any X for which P(X) is false?"</strong></p>
<p>Instead, Prolog operates on the principle of negation as failure. This means that if Prolog cannot prove a statement, it considers that statement false.</p>
<p>For example, if you ask <code>?- \+ has_job(carol).</code> (meaning "Is it not true that Carol has a job?"), Prolog will say True, because it simply cannot find any proof that Carol has a job in its knowledge base.</p>
<p>This is a significant distinction: it doesn't mean Carol definitely doesn't have a job, nor does Prolog provide a formal counterexample. It merely reflects a lack of provable information.</p>
<p>This fundamental constraint means Prolog, while powerful for logic programming, falls short of being a full-fledged proof assistant for comprehensive formal verification.</p>
<h3 id="heading-coq"><strong>Coq</strong></h3>
<p>After experimenting with Prolog and seeing its limitations, you can move on to a more powerful proof assistant like <strong>Coq</strong>. Coq is employed in <strong>safety-critical domains</strong> where absolute mathematical certainty is paramount. <code>coqtop</code> is the standard REPL for Coq.</p>
<p>A fundamental difference from Prolog is Coq's lack of a <strong>Closed World Assumption</strong>. In Coq, anything not explicitly proven is simply <strong>unknown</strong>, not automatically false.</p>
<p>Unlike Prolog, Coq's primary purpose isn't solving computational problems by searching a knowledge base. Its true power lies in its ability to <strong>construct and verify formal mathematical proofs and programs with absolute rigor</strong>. Its interaction involves managing a <strong>proof state</strong> (your remaining goals) and applying <strong>tactics</strong> (logical inference steps) until the proof is complete.</p>
<h4 id="heading-installing-coq">Installing Coq</h4>
<p>Coq can be installed in several ways, often via package managers or a tool called <code>opam</code> (the OCaml package manager, as Coq is written in OCaml).</p>
<ul>
<li><p><strong>Official Downloads:</strong> Visit the Coq website for detailed instructions for your OS: <a target="_blank" href="https://coq.inria.fr/download">https://coq.inria.fr/download</a></p>
</li>
<li><p><strong>Using a system package manager (for example, Ubuntu/Debian):</strong> Bash</p>
<pre><code class="lang-haskell">  sudo apt update
  sudo apt install coq
</code></pre>
</li>
</ul>
<h4 id="heading-using-coq-repl-vs-file">Using Coq: REPL vs. File</h4>
<ul>
<li><p><strong>REPL (</strong><code>coqtop</code>) is best for: Trying out single tactics, inspecting the current proof state, or learning basic syntax for very short commands.</p>
</li>
<li><p><strong>A File (</strong><code>.v</code> extension) is best for: <strong>Almost all Coq development and proof construction.</strong> This is how complex proofs and verified programs are structured and managed.</p>
</li>
</ul>
<h4 id="heading-coqs-comprehensive-question-answering">Coq's Comprehensive Question Answering</h4>
<p>Unlike Prolog, Coq can directly address all three types of logical questions we've discussed, providing robust answers backed by formal proof:</p>
<ul>
<li><p><strong>"Is P(X) true for a specific X?"</strong>: Coq allows you to define a precise statement (a <strong>theorem</strong>) like "Alice has a job." You then build a step-by-step logical <strong>proof</strong> that formally confirms whether this statement is true based on your definitions. If the proof succeeds, Coq formally verifies it: if it fails, Coq clearly shows where your logic breaks down.</p>
</li>
<li><p><strong>"Is there an X for which P(X) is true?"</strong>: Coq handles questions of existence. If you ask, "Does someone have a job?", you can construct a proof by explicitly providing an example (like "Alice") and then proving that your chosen example indeed satisfies the condition ("Alice has a job").</p>
</li>
<li><p><strong>"Is there any X for which P(X) is false?"</strong>: This is a key capability where Coq excels over Prolog. Coq allows you to formally prove that a statement is false, or that a counterexample exists. For instance, you could prove "Carol does not have a job" by showing it contradicts the definition, or prove "there exists someone who doesn't have a job" by explicitly identifying such a person and proving that they indeed lack a job. This direct ability to reason about negation and provide formal counterexamples (or prove their non-existence) is what makes Coq a <strong>full-fledged proof assistant</strong>.</p>
</li>
</ul>
<p>While Coq's core doesn't automatically generate counterexamples when a proof fails, plugins like QuickChick can be integrated for property-based testing to find falsifying examples.</p>
<p>It's a Coq library that allows you to specify properties about your Coq definitions and then <strong>randomly generate inputs</strong> to try and find a counterexample that falsifies your property.</p>
<p>This is a powerful way to <em>find bugs early</em> in your formalization before you invest a lot of time trying to prove a false theorem.</p>
<h3 id="heading-tla-isabelle-and-lean-a-spectrum-of-formal-verification">TLA+, Isabelle, and Lean: A Spectrum of Formal Verification</h3>
<p>Beyond Prolog and Coq, other powerful proof assistants and formal specification languages cater to different needs and paradigms:</p>
<ul>
<li><p><strong>TLA+:</strong> This is a formal <strong>specification language</strong> developed by Leslie Lamport. It focuses on modeling and verifying <strong>system designs</strong> (especially concurrent and distributed ones) using <strong>temporal logic</strong>, rather than proving low-level code. It helps ensure critical properties like safety (nothing bad ever happens) and liveness (something good eventually happens). Its practicality and accessibility make it popular in industry, notably at Amazon and Microsoft for robust system design.</p>
</li>
<li><p><strong>Isabelle and Lean:</strong> These are modern, highly advanced proof assistants.</p>
<ul>
<li><p><strong>Isabelle</strong>, grounded in higher-order logic, is widely used by researchers and institutions (for example, in projects like the seL4 verified microkernel) for formal theorem proving and software verification in academic and <strong>safety-critical domains</strong> demanding extreme rigor.</p>
</li>
<li><p><strong>Lean</strong>, based on dependent type theory, is favored by mathematicians for <strong>formalizing proofs in pure mathematics</strong> (for example, number theory, algebra). It's known for its powerful automation and active community.</p>
</li>
</ul>
</li>
</ul>
<p>These tools represent the pinnacle of applying formal logic to ensure the correctness and reliability of both mathematical theories and complex software systems.</p>
<p>Now that you have a good lay of the land in both theory and practice, here are some thought experiments to enrich your education.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749063042362/b94ec237-0aca-46d8-8921-80dfe1f5f051.jpeg" alt="nuts on a table, like almond, cashew " class="image--center mx-auto" width="6000" height="4000" loading="lazy"></p>
<h2 id="heading-food-for-thought">Food for Thought</h2>
<p>The journey into formal logic and its intersection with practical domains like software and science offers many avenues for deeper exploration.</p>
<h3 id="heading-hypothesis-testing-in-science-and-the-implication-truth-table">Hypothesis Testing in Science and the Implication Truth Table</h3>
<p>Statistical hypothesis testing uses a probabilistic form of Modus Tollens. We start with a <strong>null hypothesis (H0​): "If H0​ is true, then observing this data (or more extreme data) is likely."</strong> We then observe data that is highly unlikely/unexpected if H0​ were true (that is, a small p-value). This serves as our <strong>probabilistic "not Q."</strong> Therefore, we conclude that H0​ is likely not true (we reject H0​). This is our <strong>probabilistic "∴¬P."</strong></p>
<p>Here, the <strong>"truthiness" of P⟹Q is being tested</strong>, rather than simply assumed to be true for developing arguments, as in Modus Ponens or Modus Tollens. There's no absolute truth or anything to "prove" definitively.</p>
<p>Inferences are drawn from prior experiments (which inform the test data distribution) and context-specific experiment setups (which determine the significance level α), together defining the threshold (critical value) for what is considered an unlikely observation of Q.</p>
<p>The experiment's result is a rejection (or lack thereof) of H0​, not a definitive proof that H0​ is true.</p>
<h3 id="heading-inductive-reasonings-relationship-to-deductive-arguments">Inductive Reasoning's Relationship to Deductive Arguments</h3>
<ul>
<li><p><strong>Induction</strong> generates general rules (for example, "P is always followed by Q") from specific observations or cases.</p>
</li>
<li><p><strong>Deduction</strong> then tests or applies those general rules in new situations.</p>
</li>
</ul>
<p>If deduction leads to wrong predictions (that is, a rule is falsified), induction may need to revise the original rule, which forms a continuous <strong>feedback loop</strong> that refines our understanding.</p>
<h3 id="heading-necessity-and-sufficiency-in-implication">Necessity and Sufficiency in Implication</h3>
<p>The implication <strong>P⟹Q ("If you crossed the border, you must have had a passport")</strong> unpacks into two fundamental logical concepts:</p>
<ul>
<li><p><strong>P is sufficient for Q:</strong> Crossing the border <strong>guarantees</strong> you had a passport. (P alone is enough for Q.)</p>
</li>
<li><p><strong>Q is necessary for P:</strong> If you <strong>didn't have a passport (¬Q), you couldn't have crossed (¬P)</strong>. (Q is required for P to happen.)</p>
</li>
</ul>
<h2 id="heading-qed-the-enduring-power-of-logic-in-an-uncertain-world">Q.E.D.: The Enduring Power of Logic in an Uncertain World</h2>
<p>Throughout this handbook, we’ve journeyed from the foundational concepts of propositional logic and truth tables to the powerful argument forms of Modus Ponens and Modus Tollens. We explored how these tools enable valid deductions and identified common logical fallacies like Affirming the Consequent and Denying the Antecedent, understanding why they lead to incorrect inferences when an "if-then" relationship isn't a strict "if and only if." We learned the profound importance of falsifiability – the ability for a statement or hypothesis to be disproven – a cornerstone of both scientific inquiry and robust software testing.</p>
<p>We then delved into the practical application of these logical principles in software development, mapping code correctness to test outcomes. We discovered how a failing test, when trusted, becomes a powerful application of Modus Tollens, pinpointing defects. We also confronted the "illusion of correctness" that arises from the affirming the consequent fallacy when tests pass for the wrong reasons, especially when using test doubles.</p>
<p>Crucially, we introduced the "If and Only If" (P⟺Q) relationship, highlighting its unparalleled power in establishing unambiguous connections between cause and effect. This bidirectional guarantee is the ideal we strive for in test suite quality, moving beyond mere correlation to a deeper understanding of causality. We saw how mutation testing rigorously pushes us towards this "if and only if" confidence by actively trying to falsify the assumption that "incorrect code leads to failing tests," thereby strengthening the inverse: "passing tests guarantee correct code."</p>
<p>We also acknowledged the "messy reality" of modern software. Large systems are webs of complexity, with fan-in/fan-out patterns, side effects, and unforeseen interactions that can obscure clear logical chains. Technical debt and the double-edged sword of abstraction often mask the true origins of bugs, turning debugging into a "causal fog."</p>
<h3 id="heading-logic-as-your-compass">Logic as Your Compass</h3>
<p>Despite these formidable challenges, the logical principles we've explored remain your most vital tools. They provide the mental framework to navigate uncertainty.</p>
<p>When confronted with a bug, your ability to reason logically allows you to formulate hypotheses, design focused experiments (your tests), and interpret their outcomes with precision. Whether you're debugging a complex microservice or reasoning about a simple function, applying Modus Tollens to a failing test or designing tests that aim for P⟺Q clarity helps you cut through the noise.</p>
<p>We also touched upon advanced tools like Proof Assistants (Prolog, Coq, TLA+, Isabelle, Lean), which represent the pinnacle of applying formal logic to guarantee system correctness – a testament to the enduring power of logical rigor in critical domains.</p>
<p>In the intricate dance between theory and practice, the principles of logic stand as an unshakeable foundation. They are the "rocks" upon which you can meticulously build your understanding and your systems. The more consistently you apply this critical thinking, driven by curiosity and a commitment to rigorous validation, the clearer your path becomes.</p>
<p>This clarity is not just about fixing today’s bugs, it’s about continually refining your mental models, fostering trust in your codebase, and equipping yourself to build increasingly robust and predictable systems in an ever-evolving technological landscape.</p>
<p>If you love problem solving, critical thinking, or have experiences on how you fixed an issue that looked different from how it initially seemed, feel free to connect with me at <a target="_blank" href="https://linkedin.com/in/hanqi91">https://linkedin.com/in/hanqi91</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749064755840/c7646f6a-a8ba-4cf5-9647-0488e24705aa.jpeg" alt="man kayaking and readying for a drop down a waterfall" class="image--center mx-auto" width="2208" height="2686" loading="lazy"></p>
<h2 id="heading-resources">Resources</h2>
<ol>
<li><p>Article that motivated this handbook: <a target="_blank" href="https://thoughtbot.com/blog/classical-reasoning-and-debugging">Classical Reasoning and Debugging</a></p>
</li>
<li><p>3 Formal proofs of modus tollens: <a target="_blank" href="https://en.wikipedia.org/wiki/Modus_tollens">https://en.wikipedia.org/wiki/Modus_tollens</a></p>
</li>
<li><p>Table of 24 syllogisms: <a target="_blank" href="https://en.wikipedia.org/wiki/Syllogism">https://en.wikipedia.org/wiki/Syllogism</a></p>
</li>
<li><p>Challenging Assumptions: <a target="_blank" href="https://thoughtbot.com/blog/falsehoods-software-teams-believe-about-user-feedback">Falsehoods software teams believe about user feedback</a></p>
</li>
<li><p>How assumptions and software evolve beyond your control: <a target="_blank" href="https://www.tdda.info/why-code-rusts">https://www.tdda.info/why-code-rusts</a></p>
</li>
<li><p>Relationship to Hypothesis Testing: <a target="_blank" href="https://sites.google.com/view/reasonedwriting/home/FRAMEWORK_FOR_SCIENTIFIC_PAPERS/HYPOTHESES/HOW_TO_TEST_HYPOTHESES/MODUS_TOLLENS">https://sites.google.com/view/reasonedwriting/home/FRAMEWORK_FOR_SCIENTIFIC_PAPERS/HYPOTHESES/HOW_TO_TEST_HYPOTHESES/MODUS_TOLLENS</a></p>
</li>
<li><p>The Troubleshooting Mindset: <a target="_blank" href="https://www.autodidacts.io/troubleshooting/">https://www.autodidacts.io/troubleshooting/</a></p>
</li>
<li><p>Causal Diagrams from The Effect Book: <a target="_blank" href="https://theeffectbook.net/ch-CausalDiagrams.html">https://theeffectbook.net/ch-CausalDiagrams.html</a></p>
</li>
<li><p>A systematic guide to the mindsets and practices of debugging: <a target="_blank" href="https://www.amazon.sg/Debug-Find-Repair-Prevent-Bugs/dp/193435628X">https://www.amazon.sg/Debug-Find-Repair-Prevent-Bugs/dp/193435628X</a></p>
</li>
<li><p>Constructing P in a way to ensure software correctness: <a target="_blank" href="https://www.hillelwayne.com/post/constructive/">https://www.hillelwayne.com/post/constructive/</a></p>
</li>
<li><p>Fail Fast by explicitly representing assumptions as assertions: <a target="_blank" href="https://www.martinfowler.com/ieeeSoftware/failFast.pdf">https://www.martinfowler.com/ieeeSoftware/failFast.pdf</a></p>
</li>
<li><p>Deterministic Simulation Testing to tackle complex systems: <a target="_blank" href="https://pierrezemb.fr/posts/learn-about-dst/">https://pierrezemb.fr/posts/learn-about-dst/</a></p>
</li>
<li><p>GitHub’s Engineering System Success Playbook (ESSP) - Quality, Velocity, Developer Happiness on Business Outcomes: <a target="_blank" href="https://assets.ctfassets.net/wfutmusr1t3h/us6AUuwawrtNGTlwlT9Ac/f0fce86712054fc87f10db28b20f303b/GitHub-ESSP.pdf">https://assets.ctfassets.net/wfutmusr1t3h/us6AUuwawrtNGTlwlT9Ac/f0fce86712054fc87f10db28b20f303b/GitHub-ESSP.pdf</a></p>
</li>
<li><p>Closed-world assumption: <a target="_blank" href="https://en.wikipedia.org/wiki/Closed-world_assumption">https://en.wikipedia.org/wiki/Closed-world_assumption</a></p>
</li>
</ol>
<h2 id="heading-glossary">Glossary</h2>
<ul>
<li><p><strong>Axiom:</strong> A fundamental truth or rule accepted as a starting point for a logical or mathematical system, without requiring proof.</p>
</li>
<li><p><strong>Contrapositive:</strong> A logically equivalent form of an "if-then" statement (P⟹Q), which is ¬Q⟹¬P ("If not Q, then not P").</p>
</li>
<li><p><strong>Deductive Reasoning:</strong> A type of logical reasoning where a conclusion is necessarily true if its premises are true.</p>
</li>
<li><p><strong>Falsification:</strong> The principle, especially in science (from Karl Popper), that a hypothesis or theory must be capable of being proven false by empirical observation or experiment.</p>
</li>
<li><p><strong>Formal Logic:</strong> The study of abstract systems of reasoning and arguments based on their structure, independent of content.</p>
</li>
<li><p><strong>Hypothesis Testing:</strong> A statistical method for making inferences about a population based on sample data, typically by testing a null hypothesis (e.g., "P has no effect on Q") against an alternative hypothesis.</p>
</li>
<li><p><strong>Logical Fallacy:</strong> A flaw in the structure or content of an argument that makes it unsound or invalid, even if its conclusion might seem plausible.</p>
<ul>
<li><p><strong>Affirming the Consequent (Fallacy):</strong> An invalid argument form that mistakenly assumes if P⟹Q is true, and Q is true, then P must be true.</p>
</li>
<li><p><strong>Denying the Antecedent (Fallacy):</strong> An invalid argument form that mistakenly assumes if P⟹Q is true, and P is false, then Q must be false.</p>
</li>
</ul>
</li>
<li><p><strong>Modus Ponens:</strong> A valid argument form: If P⟹Q is true and P is true, then Q must be true.</p>
</li>
<li><p><strong>Modus Tollens:</strong> A valid argument form: If P⟹Q is true and ¬Q is true, then ¬P must be true.</p>
</li>
<li><p><strong>Mutation Testing:</strong> A software testing technique that involves deliberately introducing small, single-point faults (mutations) into code to assess the effectiveness and coverage of a test suite.</p>
</li>
<li><p><strong>Propositional Logic:</strong> A branch of logic that deals with propositions and their relationships using logical operators.</p>
</li>
<li><p><strong>Test-Driven Development (TDD):</strong> A software development methodology where tests are written <em>before</em> the code, guiding the development process and ensuring correctness.</p>
</li>
<li><p><strong>Truth Table:</strong> A table that systematically lists all possible truth values for a set of propositions and shows the resulting truth value of a complex logical statement.</p>
</li>
<li><p><strong>Vacuously True:</strong> Describes an implication (P⟹Q) that is considered true simply because its antecedent (P) is false.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Logic in JavaScript – Operators, Conditions, Truthy/Falsy, and More ]]>
                </title>
                <description>
                    <![CDATA[ JavaScript is a versatile programming language that empowers developers to create dynamic and interactive web pages.  One of the foundational elements of JavaScript programming is the application of logical operations to make decisions and control pr... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/logic-in-javascript/</link>
                <guid isPermaLink="false">66c4c4048e05d0e4345147cc</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ logic ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Joan Ayebola ]]>
                </dc:creator>
                <pubDate>Wed, 29 Nov 2023 21:50:16 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/Beige-aesthetic-thesis-defense-presentation.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>JavaScript is a versatile programming language that empowers developers to create dynamic and interactive web pages. </p>
<p>One of the foundational elements of JavaScript programming is the application of logical operations to make decisions and control program flow. </p>
<p>In this guide, we'll delve into the basics of logical operations in JavaScript. I'll provide simple explanations and ample code snippets to make these concepts easier to understand.</p>
<h2 id="heading-understanding-logical-operators">Understanding Logical Operators</h2>
<p>Logical operators in JavaScript let developers perform operations on values or expressions, playing a crucial role in effective decision-making within code. </p>
<p>The primary logical operators are <code>&amp;&amp;</code> (AND), <code>||</code> (OR), and <code>!</code> (NOT). Let's look at each one now.</p>
<h3 id="heading-1-and-ampamp-operator">1. AND (<code>&amp;&amp;</code>) Operator</h3>
<p>The AND (<code>&amp;&amp;</code>) operator in JavaScript is a logical operator that combines two or more conditions. It returns <code>true</code> only if all the conditions being evaluated are <code>true</code>. If any of the conditions is <code>false</code>, the entire expression evaluates to <code>false</code>.</p>
<h3 id="heading-example">Example:</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isSunny = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">let</span> isWarm = <span class="hljs-literal">true</span>;

<span class="hljs-keyword">if</span> (isSunny &amp;&amp; isWarm) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Perfect weather for outdoor activities!"</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Maybe another day."</span>);
}
</code></pre>
<p>In this example, the <code>isSunny &amp;&amp; isWarm</code> condition must be true for the message about perfect weather to be displayed. If either <code>isSunny</code> or <code>isWarm</code> is <code>false</code>, the <code>else</code> block is executed.</p>
<p>Let's look at some scenarios when the AND operator is particularly useful.</p>
<p><strong>When combining conditions:</strong> Use <code>&amp;&amp;</code> when you want an action to be taken only when multiple conditions are met simultaneously.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (userIsLoggedIn &amp;&amp; userHasPermission) {
  <span class="hljs-comment">// Perform a privileged action.</span>
} <span class="hljs-keyword">else</span> {
  <span class="hljs-comment">// Display an error message or redirect to login.</span>
}
</code></pre>
<p><strong>In guard clauses:</strong> Use <code>&amp;&amp;</code> in guard clauses to ensure that certain conditions are met before proceeding with further code execution.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">performAction</span>(<span class="hljs-params">user</span>) </span>{
  <span class="hljs-keyword">if</span> (!user || !user.isLoggedIn) {
    <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Exit early if the user is not logged in.</span>
  }

  <span class="hljs-comment">// Continue with the action for logged-in users.</span>
}
</code></pre>
<p><strong>For form validation:</strong> In scenarios like form validation, you might use <code>&amp;&amp;</code> to check multiple conditions before allowing form submission.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (isUsernameValid &amp;&amp; isPasswordValid &amp;&amp; isEmailValid) {
  <span class="hljs-comment">// Submit the form.</span>
} <span class="hljs-keyword">else</span> {
  <span class="hljs-comment">// Display an error message.</span>
}
</code></pre>
<p>The AND operator is useful when you want to ensure that all specified conditions are true before proceeding with a particular action or decision in your code. It's a fundamental tool for creating more nuanced and context-specific logic in your JavaScript programs.</p>
<h3 id="heading-2-or-operator">2. OR (<code>||</code>) Operator</h3>
<p>The OR (<code>||</code>) operator in JavaScript is a logical operator that returns <code>true</code> if at least one of the conditions it connects is <code>true</code>. It is often used when you want an action to occur if any one of multiple conditions is met.</p>
<p>Here's a basic example to illustrate the OR operator:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> hasCoffee = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">let</span> hasTea = <span class="hljs-literal">false</span>;

<span class="hljs-keyword">if</span> (hasCoffee || hasTea) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"You can enjoy a hot beverage!"</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"No hot beverage available."</span>);
}
</code></pre>
<p>In this example, the <code>hasCoffee || hasTea</code> condition is <code>true</code> because <code>hasCoffee</code> is <code>true</code>. As a result, the message "You can enjoy a hot beverage!" will be logged.</p>
<p>Here are some scenarios where you might want to use the OR operator:</p>
<p><strong>Fallback Values:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> userInput = <span class="hljs-string">""</span>; <span class="hljs-comment">// User didn't provide a value</span>
<span class="hljs-keyword">let</span> username = userInput || <span class="hljs-string">"Guest"</span>;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Welcome, "</span> + username);
</code></pre>
<p>In this case, if the user didn't provide a username (<code>userInput</code> is an empty string), the OR operator assigns a default value of "Guest" to <code>username</code>. This is a common pattern for providing fallback or default values.</p>
<p><strong>Checking for Multiple Conditions:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isWeekend = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> isHoliday = <span class="hljs-literal">true</span>;

<span class="hljs-keyword">if</span> (isWeekend || isHoliday) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"It's time for a break!"</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Back to work."</span>);
}
</code></pre>
<p>This example uses the OR operator to check if it's either the weekend or a holiday, indicating that it's time for a break.</p>
<p><strong>Form Validation:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> username = <span class="hljs-string">"john_doe"</span>;
<span class="hljs-keyword">let</span> password = <span class="hljs-string">""</span>;

<span class="hljs-keyword">if</span> (username &amp;&amp; password) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Form submitted successfully!"</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Please fill in both username and password."</span>);
}
</code></pre>
<p>Here, the OR operator can be used to check if either the username or password is missing. If either condition is <code>true</code>, it prompts the user to fill in both fields.</p>
<p>The OR operator is useful when you want an action to occur if at least one of the specified conditions is true. It's commonly employed in scenarios involving fallback values, checking multiple conditions, or form validation where any of several fields need to be filled.</p>
<h3 id="heading-3-not-operator">3. NOT (<code>!</code>) Operator</h3>
<p>The NOT (<code>!</code>) operator in JavaScript is a unary operator that negates the truthiness of a value. It's used to invert a boolean value or a truthy/falsy expression. In simpler terms, it turns <code>true</code> into <code>false</code> and <code>false</code> into <code>true</code>. Here's how it works:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isSunny = <span class="hljs-literal">true</span>;

<span class="hljs-comment">// Using NOT operator to invert the value</span>
<span class="hljs-keyword">let</span> isNotSunny = !isSunny;

<span class="hljs-built_in">console</span>.log(isNotSunny); <span class="hljs-comment">// Output: false</span>
</code></pre>
<p>Now, let's discuss when you might want to use the NOT operator:</p>
<p><strong>Checking for Negation:</strong> The most straightforward use of the NOT operator is when you want to check for the negation of a condition. For example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isRaining = <span class="hljs-literal">false</span>;

<span class="hljs-keyword">if</span> (!isRaining) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"It's not raining. Enjoy the day!"</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Don't forget your umbrella!"</span>);
}
</code></pre>
<p>In this case, the <code>!isRaining</code> condition is true when it's not raining. It provides a concise way of expressing the idea that it's a good day when it's not raining.</p>
<p><strong>Checking for Falsy Values:</strong> The NOT operator is often used to check if a value is falsy. Remember that in JavaScript, certain values are considered falsy, such as <code>false</code>, <code>0</code>, <code>null</code>, <code>undefined</code>, <code>NaN</code>, and an empty string <code>""</code>. The NOT operator can be handy for checking whether a variable holds a falsy value:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> userRole = <span class="hljs-literal">null</span>;

<span class="hljs-keyword">if</span> (!userRole) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"User role is not defined. Assigning a default role."</span>);
  userRole = <span class="hljs-string">"Guest"</span>;
}
</code></pre>
<p>In this example, if <code>userRole</code> is <code>null</code> (a falsy value), the condition <code>!userRole</code> evaluates to <code>true</code>, and a default role is assigned.</p>
<p><strong>Creating Clearer Conditions:</strong> The NOT operator can also be used to make conditions more explicit or readable. For instance:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isLoggedIn = <span class="hljs-literal">false</span>;

<span class="hljs-keyword">if</span> (!isLoggedIn) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"User is not logged in. Redirect to login page."</span>);
}
</code></pre>
<p>This condition checks if the user is not logged in and takes action accordingly.</p>
<p>The NOT operator is useful when you need to negate a boolean value or check for falsy values, providing a concise and readable way to express conditions in your JavaScript code.</p>
<h2 id="heading-how-to-combine-logical-operators">How to Combine Logical Operators</h2>
<p>You can combine logical operators to create more complex conditions, introducing parentheses to control the order of evaluation.  </p>
<p>Let's consider an example where we want to determine if a person is eligible to enter a club based on their age and whether they have a valid ID. The conditions for entry are as follows:</p>
<ul>
<li>The person must be at least 18 years old.</li>
<li>If the person is between 16 and 18 years old, they can enter only if they have a valid ID.</li>
<li>If the person is under 16, entry is not allowed.</li>
</ul>
<p>Here's the JavaScript code for this scenario:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> age = <span class="hljs-number">17</span>;
<span class="hljs-keyword">let</span> hasValidID = <span class="hljs-literal">false</span>;

<span class="hljs-keyword">if</span> ((age &gt;= <span class="hljs-number">18</span>) || (age &gt;= <span class="hljs-number">16</span> &amp;&amp; hasValidID)) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Welcome to the club!"</span>);
} <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Entry not allowed."</span>);
}
</code></pre>
<p>In this code:</p>
<ul>
<li><code>age</code> is set to <code>17</code>, indicating that the person is 17 years old.</li>
<li><code>hasValidID</code> is set to <code>false</code>, indicating that the person does not have a valid ID.</li>
</ul>
<p>Now, let's evaluate the condition within the <code>if</code> statement:</p>
<ol>
<li><code>(age &gt;= 18)</code> evaluates to <code>false</code> because the person is not 18 or older.</li>
<li><code>(age &gt;= 16 &amp;&amp; hasValidID)</code> evaluates to <code>true &amp;&amp; false</code>, which is <code>false</code>. This is because the person is 17, which satisfies the first part of the condition, but they don't have a valid ID.</li>
</ol>
<p>Since both parts of the condition are <code>false</code>, the code block inside the <code>else</code> statement is executed, resulting in the output:</p>
<pre><code>Entry not allowed.
</code></pre><p>This example demonstrates how logical operators can be combined to create complex conditions, allowing you to control the flow of your program based on various factors.</p>
<h2 id="heading-conditional-statements">Conditional Statements</h2>
<p>Logical operators are frequently employed in conditional statements (<code>if</code>, <code>else if</code>, and <code>else</code>) to dictate program flow based on specific conditions.</p>
<h3 id="heading-1-if-statement">1. <strong>if Statement:</strong></h3>
<p>The <code>if</code> statement in JavaScript is used to execute a block of code if a specified condition is true. Logical operators often play a crucial role in defining these conditions.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isHungry = <span class="hljs-literal">true</span>;
<span class="hljs-keyword">let</span> hasFood = <span class="hljs-literal">true</span>;

<span class="hljs-keyword">if</span> (isHungry &amp;&amp; hasFood) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Let's have a meal!"</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"No need for a meal right now."</span>);
}
</code></pre>
<p>In this example, the <code>&amp;&amp;</code> (AND) operator combines two conditions (<code>isHungry</code> and <code>hasFood</code>). The block of code inside the <code>if</code> statement will execute only if both conditions are true. If either <code>isHungry</code> or <code>hasFood</code> is false, the code inside the <code>else</code> block will run.</p>
<h3 id="heading-2-else-statement">2. <strong>else Statement:</strong></h3>
<p>The <code>else</code> statement is paired with the <code>if</code> statement to execute a block of code when the specified condition is false.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isNight = <span class="hljs-literal">true</span>;

<span class="hljs-keyword">if</span> (isNight) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"It's nighttime. Sleep tight!"</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"It's daytime. Enjoy your day!"</span>);
}
</code></pre>
<p>Here, the <code>if</code> statement checks if <code>isNight</code> is true. If it is, the corresponding message is printed. If <code>isNight</code> is false, the <code>else</code> block is executed, providing an alternative message for daytime.</p>
<h3 id="heading-3-else-if-statement">3. <strong>else if Statement:</strong></h3>
<p>The <code>else if</code> statement accommodates multiple conditions, allowing for more complex decision-making.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> timeOfDay = <span class="hljs-string">"morning"</span>;

<span class="hljs-keyword">if</span> (timeOfDay === <span class="hljs-string">"morning"</span>) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Good morning!"</span>);
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (timeOfDay === <span class="hljs-string">"afternoon"</span>) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Good afternoon!"</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Good evening!"</span>);
}
</code></pre>
<p>In this case, the code greets users differently based on the value of <code>timeOfDay</code>. The <code>===</code> operator is used for strict equality comparison, and logical operators like <code>&amp;&amp;</code> or <code>||</code> can be incorporated to form more intricate conditions.</p>
<p>These examples illustrate how logical operators are employed within <code>if</code>, <code>else</code>, and <code>else if</code> statements to control the flow of a JavaScript program based on specific conditions.</p>
<h2 id="heading-ternary-operator">Ternary Operator</h2>
<p>The ternary operator, often denoted by <code>? :</code>, provides a concise way to express conditional statements. It's a shorthand version of an <code>if-else</code> statement. The basic syntax is:</p>
<pre><code class="lang-javascript">condition ? expression_if_true : expression_if_false;
</code></pre>
<p>Here's a breakdown of the components:</p>
<ul>
<li><code>condition</code>: a boolean expression that is evaluated. If it is true, the expression before the <code>:</code> is executed – otherwise, the expression after the <code>:</code> is executed.</li>
<li><code>expression_if_true</code>: the value or expression returned if the condition is true.</li>
<li><code>expression_if_false</code>: the value or expression returned if the condition is false.</li>
</ul>
<p>Now, let's take a closer look at the example provided:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> weather = isSunny ? <span class="hljs-string">"Enjoy the sunshine!"</span> : <span class="hljs-string">"Grab an umbrella!"</span>;
</code></pre>
<p>In this example:</p>
<ul>
<li><code>isSunny</code> is the condition being checked. If <code>isSunny</code> is true, the value of the entire expression becomes "Enjoy the sunshine!". If <code>isSunny</code> is false, the value becomes "Grab an umbrella!".</li>
<li>The <code>?</code> is like asking a question: "Is it sunny?" If the answer is yes, then "Enjoy the sunshine!" is the response (before the <code>:</code>). If the answer is no, then "Grab an umbrella!" is the response (after the <code>:</code>).</li>
</ul>
<p>This can be seen as a shorthand way of writing an <code>if-else</code> statement. The equivalent <code>if-else</code> statement for the example would be:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> weather;
<span class="hljs-keyword">if</span> (isSunny) {
  weather = <span class="hljs-string">"Enjoy the sunshine!"</span>;
} <span class="hljs-keyword">else</span> {
  weather = <span class="hljs-string">"Grab an umbrella!"</span>;
}
</code></pre>
<p>Both the ternary operator and the <code>if-else</code> statement achieve the same result, but the ternary operator is more concise and is often used for simple conditional assignments. </p>
<p>It's important to note that using the ternary operator excessively or in complex scenarios can reduce code readability, so it's best used for straightforward conditions.</p>
<h2 id="heading-switch-statement">Switch Statement</h2>
<p>The <code>switch</code> statement handles multiple conditions efficiently, particularly when there are several possible values for a variable. Extending our day-of-week example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> dayOfWeek = <span class="hljs-string">"Wednesday"</span>;

<span class="hljs-keyword">switch</span> (dayOfWeek) {
  <span class="hljs-keyword">case</span> <span class="hljs-string">"Monday"</span>:
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"It's the beginning of the week."</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> <span class="hljs-string">"Wednesday"</span>:
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"It's the middle of the week."</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> <span class="hljs-string">"Friday"</span>:
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"It's the end of the week."</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">default</span>:
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"It's an ordinary day."</span>);
}
</code></pre>
<p>Here, the <code>switch</code> statement triggers the appropriate message based on the day of the week.</p>
<h2 id="heading-short-circuit-evaluation">Short-Circuit Evaluation</h2>
<p>JavaScript leverages short-circuit evaluation with logical operators, optimizing performance by halting evaluation once the result is determined.</p>
<h3 id="heading-example-1-short-circuit-with-ampamp-operator">Example 1: Short-Circuit with <code>&amp;&amp;</code> Operator</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isTrue = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> result = isTrue &amp;&amp; someFunction();

<span class="hljs-built_in">console</span>.log(result); <span class="hljs-comment">// `someFunction()` is not called if `isTrue` is false</span>
</code></pre>
<p>In this example, <code>someFunction()</code> is only called if <code>isTrue</code> is true, showcasing the efficiency of short-circuit evaluation.</p>
<h3 id="heading-example-2-short-circuit-with-operator">Example 2: Short-Circuit with <code>||</code> Operator</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> isLoggedIn = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">let</span> username = isLoggedIn || <span class="hljs-string">"Guest"</span>;

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Welcome, "</span> + username); <span class="hljs-comment">// If not logged in, the default username is "Guest"</span>
</code></pre>
<p>Here, <code>username</code> is assigned the default value "Guest" only if the user is not logged in, thanks to short-circuit evaluation.</p>
<h2 id="heading-truthy-and-falsy-values">Truthy and Falsy Values</h2>
<p>In JavaScript, logical operators can be used with non-boolean values. Understanding truthy and falsy values is crucial in such scenarios.</p>
<h3 id="heading-truthy-and-falsy-values-overview">Truthy and Falsy Values Overview</h3>
<p>Every value in JavaScript has inherent truthiness or falsiness. Falsy values include <code>false</code>, <code>0</code>, <code>null</code>, <code>undefined</code>, <code>NaN</code>, and an empty string (<code>""</code>). Truthy values encompass all values not explicitly falsy.</p>
<h3 id="heading-example-truthy-and-falsy-values">Example: Truthy and Falsy Values</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> userRole = <span class="hljs-string">""</span>; <span class="hljs-comment">// An empty string is falsy</span>

<span class="hljs-keyword">let</span> roleMessage = userRole || <span class="hljs-string">"User"</span>;

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"You are a "</span> + roleMessage); <span class="hljs-comment">// If `userRole` is falsy, default to "User"</span>
</code></pre>
<p>Here, the default value "User" is assigned to <code>roleMessage</code> only if <code>userRole</code> is falsy.</p>
<h2 id="heading-summary-table">Summary Table</h2>
<p>Let's provide a quick reference for the different logical operators:</p>
<table>
<thead>
<tr>
<th>Operator</th>
<th>Symbol</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>AND</td>
<td><code>&amp;&amp;</code></td>
<td>Returns true if all conditions are true.</td>
</tr>
<tr>
<td>OR</td>
<td><code>||</code></td>
<td>Returns true if at least one condition is true.</td>
</tr>
<tr>
<td>NOT</td>
<td><code>!</code></td>
<td>Inverts the result of a logical expression.</td>
</tr>
</tbody>
</table>

<h2 id="heading-practical-applications">Practical Applications</h2>
<p>Logical operators play a crucial role in real-world JavaScript applications. Here are some practical examples:</p>
<h3 id="heading-form-validation">Form Validation</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> username = <span class="hljs-string">"JohnDoe"</span>;
<span class="hljs-keyword">let</span> password = <span class="hljs-string">"secretp@ss"</span>;

<span class="hljs-keyword">if</span> (username &amp;&amp; password) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Form submitted successfully."</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Please enter both username and password."</span>);
}
</code></pre>
<p>In this scenario, the form submission is validated by ensuring both the username and password are provided.</p>
<h3 id="heading-responsive-ui">Responsive UI</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> screenWidth = <span class="hljs-number">800</span>;

<span class="hljs-keyword">if</span> (screenWidth &gt; <span class="hljs-number">600</span> &amp;&amp; screenWidth &lt;= <span class="hljs-number">1024</span>) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Displaying a tablet-friendly layout."</span>);
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (screenWidth &gt; <span class="hljs-number">1024</span>) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Displaying a desktop layout."</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Displaying a mobile-friendly layout."</span>);
}
</code></pre>
<p>Logical operators are often used to determine the layout based on the screen width, creating a responsive user interface.</p>
<h3 id="heading-access-control">Access Control</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> userRole = <span class="hljs-string">"admin"</span>;
<span class="hljs-keyword">let</span> isLoggedIn = <span class="hljs-literal">true</span>;

<span class="hljs-keyword">if</span> (userRole === <span class="hljs-string">"admin"</span> &amp;&amp; isLoggedIn) {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Access granted to admin dashboard."</span>);
} <span class="hljs-keyword">else</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Access denied."</span>);
}
</code></pre>
<p>Logical operators help control access by verifying both the user role and login status.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Mastering logical operators is integral to writing effective and meaningful JavaScript code. Whether you're creating conditions, making decisions, or controlling program flow, logical operators are essential tools. </p>
<p>By exploring these concepts through numerous examples, you're well-equipped to apply them in your projects. Additionally, understanding truthy and falsy values enhances your ability to work with non-boolean contexts. </p>
<p>Use this guide as a foundation for writing clear and concise JavaScript, and you'll be on your way to building robust and responsive web applications. Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Propositional Logic for Beginners – You Already Know More Than You Think ]]>
                </title>
                <description>
                    <![CDATA[ It may sound surprising, but you already have all it takes to produce high-level logic: right behind your eyes.  In the first term of college, I was introduced to a subject called Logic. In the "I think, therefore I am" fashion, you might think this ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/propositional-logic-for-beginners/</link>
                <guid isPermaLink="false">66b0a7b7b27f15178972301a</guid>
                
                    <category>
                        <![CDATA[ beginners guide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ logic ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Rosa ]]>
                </dc:creator>
                <pubDate>Tue, 11 Jan 2022 18:41:47 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/01/pexels-ketut-subiyanto-4473569.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>It may sound surprising, but you already have all it takes to produce high-level logic: right behind your eyes. </p>
<p>In the first term of college, I was introduced to a subject called Logic. In the "I think, therefore I am" fashion, you might think this would be an easy subject. After all, we are all taught how to think throughout school and, since all of us "think", there would be nothing unfamiliar in the subject that could trick the less-than-alert mind. </p>
<p>Shockingly enough, in the same way <a target="_blank" href="https://www.thoughtco.com/why-math-seems-more-difficult-for-some-students-1857216">people have a hard time with Math in college</a>, Logic was the subject back then where students failed the most.</p>
<h2 id="heading-why-is-propositional-logic-hard">Why is Propositional Logic Hard?</h2>
<p>What could be the reason for that? The only thing, in theory, that might prove troublesome was the amount of unfamiliar terminology involved in propositional logic. </p>
<p>The professors used Latin expressions like "<em>modus ponens</em>" and "<em>modus tollens</em>" to define some sort of processes of inferring a result, and these often caused people to complain about the "esoteric nature" of the subject.</p>
<p>We can call this metalanguage. In this case, it can be roughly translated as "fancy language to talk about a topic that makes it harder for the students to understand what the thing really is". And it has its charm in the sense of categorizing things.</p>
<p>Intense practice with examples, however, can help students learn much more quickly than filling up their hard drive with vocabulary they'll probably only use to pass the tests and then never use again – except in those word game puzzles.</p>
<p>This is the point where I get in. Having had good results in Logic back then, I was shocked with how my classmates talked about Logic on social media (sentences like "this is not a topic for first term – it's too hard", or "why do they expect me to learn Latin? This is the 21<sup>st</sup> century!" were common). </p>
<p>Fortunately for me, instead of focusing on the naming of each inference rule I heard of, I decided to focus on understanding what each one led to – which probably made me worry less about the topic than my peers.</p>
<p><em>Ergo</em>… 😋 just kidding. SO, let me try to show you in this article some of these rules of propositional logic. </p>
<p>I will try to avoid the fancy metalanguage and present you with a couple of examples to help show you that you already think the way the Logic subject presents to you. </p>
<p>You just skip the part of using a fancy name and giving a complicated explanation for the process itself.</p>
<h2 id="heading-modus-ponens-modus-tollens-hocus-pocus-abracadabra">Modus Ponens, Modus Tollens, Hocus Pocus, Abracadabra...</h2>
<p>Let's begin with the really tough ancient Latin names: <em>Modus Ponens</em> and <em>Modus Tollens</em>. Modus ponens is defined in <a target="_blank" href="https://en.wikipedia.org/wiki/Modus_ponens">Wikipedia</a> as follows:</p>
<blockquote>
<p>In propositional logic, <strong>modus ponens</strong> (/ˈmoʊdəs ˈpoʊnɛnz/; MP), also known as <strong>modus ponendo ponens</strong> (Latin for "method of putting by placing") or implication elimination or affirming the antecedent, is a deductive argument form and rule of inference. It can be summarized as "P implies Q. P is true. Therefore, Q must also be true."</p>
</blockquote>
<p>Now I don't know about you, but when I see phonetic symbols and words that I have no idea what they mean – but someone is telling me I should – my heart skips a beat. </p>
<p>In fact, we could go straight to the last two sentences here to get to the point. The first long and haunting sentence basically serves to tell you "heroes, there is a dark and menacing-looking forest ahead of you. Leave behind all hope, ye who enter." </p>
<p>The bottom line, though, is "when the first thing is true, the second is, too. Since we know that the first is true, what do you make of the second?"</p>
<p>See? After beating the Latin words and hacking and slashing through 'propositional', 'implication', 'antecedent', 'deductive', and 'inference', you just come to the conclusion something is true because another one is true as well. Great! Now moving on.</p>
<h3 id="heading-example-of-modus-ponens">Example of modus ponens</h3>
<p>As I mentioned before, I believe examples work better than fancy words, so let's come up with a simple example:</p>
<p>I'm from a country where there is no snow at all, ever. When I'm in the US during the winter, I love making snow angels. It's winter, and I'm in the US. What do you think I'll do when it snows?</p>
<p>If what you thought was "you'll make snow angels, duh", congratulations! You have just gone through the propositional logic we talked about earlier. And you did not have to say a prayer in Latin, use magic words, cast a spell, or anything else. 😊</p>
<h2 id="heading-peas-and-queues">Peas and Queues</h2>
<p>Now you may ask, "ok, but what about the P and Q thing in the last two sentences of the definition you shamelessly CTRL-C-ed and CTRL-V-ed from Wiki?"</p>
<p>Good catch! These are representations of what we call <em>propositions</em> (that's why this is called "<em>propositional</em> logic", by the way). </p>
<p>A proposition is nothing more than a sentence. They could have said "sentence A" and "sentence B" instead. But someone in the past chose P and Q, just like in Math you'd go with X and Y. </p>
<p>Using the example above, being in the US in the winter (proposition P), to me, means (implies) that I must get to the ground and wave my arms and legs frantically to make what looks like the shape of an angel. </p>
<p>Now, you know I'm in the US in winter and it snows (I told you that "P" is true). Here is the moment where we conclude that I'll do what I say I do whenever the first sentence is true – I'll act according to the second proposition ("Q") and make it true as well.</p>
<p>Now that you know how silly the author of this article is and understand the first rule better, let's move on to the next rule, <em>avara kedavra</em>… I mean, <em>modus tollens</em>.</p>
<p>Here, <a target="_blank" href="https://en.wikipedia.org/wiki/Modus_tollens">Wikipedia</a> "helps" us once again with a beautiful, wordy definition:</p>
<blockquote>
<p>In propositional logic, <strong>modus tollens</strong> (/ˈmoʊdəs ˈtɒlɛnz/) (MT), also known as <strong>modus tollendo tollens</strong> (Latin for "method of removing by taking away") and denying the consequent, is a deductive argument form and a rule of inference.   </p>
<p><strong>Modus tollens</strong> takes the form of "If P, then Q. Not Q. Therefore, not P." It is an application of the general truth that if a statement is true, then so is its contrapositive. The form shows that inference from P implies Q to the negation of Q implies the negation of P is a valid argument.</p>
</blockquote>
<p>If you did not love going through 'denying the consequent' and 'so is its contrapositive', you are a heartless human being. </p>
<h3 id="heading-example-of-modus-tollens">Example of modus tollens</h3>
<p>Again, here comes Mighty Mouse to save the day and tell you to focus on <em>"If P, then Q. Not Q. Therefore, not P."</em> </p>
<p>What this rule is saying is actually a complement of what the first one says. If the second sentence is not true, then the first probably isn't, either. Thus, if I'm not making snow angels now, what would you make of it?</p>
<p>Since we have paired the idea of being in the US in the winter and making snow angels, at least some part of the first proposition can't be true: either I'm not in the US or it's not winter. </p>
<p>Anyways, since, for the first proposition to be true, these two parts have to be, we can assume that the first sentence is, somehow, false.</p>
<p>And just like that you realize you already know two propositional logic rules without even having to consult your dictionary! 😃</p>
<h2 id="heading-in-summary">In summary</h2>
<p>In this article, we saw that it is possible to know (and practice) logic without even studying it. We also learned that we already know more logic than we might imagine and that the author of this article loves playing in the snow. </p>
<p>As a bonus, we practiced two rules of propositional logic, whose fancy names might scare you from even looking at them: <em>modus ponens</em> and <em>modus tollens</em>.</p>
<p>There are others, though. If you liked the way they were explained here and would like to see more logical rules explained in a form you will definitely understand – and possibly realize you already make use of – send your feedback to yours truly on <a target="_blank" href="https://twitter.com/Daniel__Rosa">Twitter</a>. I'd love to walk you through the other rules as well – and maybe share more of my silliness through examples.</p>
<p>Happy coding! 😉</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Solve Einstein's Five House Riddle ]]>
                </title>
                <description>
                    <![CDATA[ I recently learned about a logic puzzle online that apparently only 2% of people can solve. There are a few different incarnations of it – some have slightly different wording, different names, or change the items in the riddle slightly. But they are... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/einsteins-riddle/</link>
                <guid isPermaLink="false">66bc55ced94fa6cb67b8451b</guid>
                
                    <category>
                        <![CDATA[ logic ]]>
                    </category>
                
                    <category>
                        <![CDATA[ puzzles ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kealan Parr ]]>
                </dc:creator>
                <pubDate>Wed, 08 Sep 2021 15:36:22 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/09/Capture.JPG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I recently learned about a logic puzzle online that apparently only 2% of people can solve.</p>
<p>There are a few different incarnations of it – some have slightly different wording, different names, or change the items in the riddle slightly. But they are all the exact same core problem.</p>
<p>The riddle itself is used as a benchmark in the evaluation of <a target="_blank" href="https://en.wikipedia.org/wiki/Constraint_satisfaction_problem">constraint satisfaction problems</a> for computer algorithms.</p>
<h2 id="heading-what-is-the-einstein-riddle">What is the Einstein Riddle?</h2>
<p>Even the origin of it the riddle is a little unclear. It's famously known as the <strong>Einstein Riddle</strong> because it was supposedly created by Einstein as a young man for fun. Others say it was used by Einstein to only select the smartest PhD students he would supervise. </p>
<p>But there are some claims online that it actually was invented by the author of <em>Alice's Adventures in Wonderland</em>, <a target="_blank" href="https://en.wikipedia.org/wiki/Lewis_Carroll">Lewis Carrol</a>.</p>
<p>It's highly unlikely that it was written by Einstein, but that doesn't really matter. What's important is that, with a basic understanding of truth tables (and a bit of patience), you can solve it, too.</p>
<h2 id="heading-how-to-solve-einsteins-riddle">How to Solve Einstein's Riddle</h2>
<p>I'm now going to give you a list of clues, and then you will need to answer a question at the end of the clues.</p>
<p>Just to be absolutely clear, all the clues are enough for you to solve it. You don't need any extra hints, and there aren't any assumptions I expect you to know.</p>
<blockquote>
<p>There are 5 houses painted five different colors.<br>In each house lives a person with a different nationality.<br>These five owners drink a certain type of beverage, smoke a certain brand of cigar, and keep a certain pet.<br>No owners have the same pet, smoke the same brand of cigar, or drink the same beverage.</p>
</blockquote>
<ul>
<li>The Brit lives in the red house</li>
<li>The Swede keeps dogs as pets</li>
<li>The Dane drinks tea</li>
<li>The green house is on the left of the white house</li>
<li>The person who smokes Pall Malls rears birds</li>
<li>The owner of the yellow house smokes Dunhill</li>
<li>The green house’s owner drinks coffee</li>
<li>The man living in the center house drinks milk</li>
<li>The Norwegian lives in the first (leftmost) house</li>
<li>The man who smokes Blends lives next to the one who keeps cats</li>
<li>The man who keeps horses lives next to the man who smokes Dunhill</li>
<li>The owner who smokes BlueMaster drinks beer</li>
<li>The German smokes Princes</li>
<li>The Norwegian lives next to the blue house</li>
<li>The man who smokes Blends has a neighbor who drinks water</li>
</ul>
<p>Now to solve, <strong>tell me who owns the fish</strong>?</p>
<p>I solved it, but it did take me a couple attempts and a bit of scribbling on paper.</p>
<h2 id="heading-how-i-approached-the-problem">How I Approached the Problem</h2>
<p>To solve the problem, the first thing I did was to try and group together the clues. There are two references to the green house in the clues, so I tried to "solve" and consider those two clues together when I was able to.  </p>
<p>I then also filled out the center house's beverage as one clue immediately tells you, and I was also able to immediately fill out the leftmost house's nationality.</p>
<p>I essentially drew a really basic grid and eliminated and filled in possibilities based initially just on the clues. Then as I filled in more, I was able to fill in more details on other houses.  </p>
<p>I don't want to keep going with the hints if you want to solve this for yourself, but that's a starting point.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-29.png" alt="Image" width="600" height="400" loading="lazy">
<em>A screenshot of part of a 5x5 table with every different possibility of Nationality, Color, Beverage, Pet and Smoking Cigar that can be removed on click.</em></p>
<p>To try and make it easier for anyone who wants to solve it or check their answer, I have made a basic site you can find here: <a target="_blank" href="http://einsteins-riddle.com/">http://einsteins-riddle.com/</a> – the screenshot from above is a part of the grid on the site.</p>
<p>On that site, you'll find a table with all the options laid out as clickable buttons. The grid is initially filled out with all the possibilities, and as you learn more you can remove possibilities until eventually there is only one option left. </p>
<p>At the bottom is a "Check Answer" button that will evaluate what you have left on your grid.</p>
<p>Try and solve it and see how you get on! If you prefer to do it via paper please do so.</p>
<p>I wish you luck 😊</p>
<p>If it stumps you, and you want to know how to solve it, you can find the solution <a target="_blank" href="https://udel.edu/~os/riddle-solution.html">here</a>.</p>
<h2 id="heading-why-are-truth-tables-helpful">Why are Truth Tables Helpful?</h2>
<p>I enjoy trying to work through these truth table problems, as it helps improve my clarity of thought. </p>
<p>Sometimes when I'm coding and need to carefully consider some complex Boolean states in my code (not <strong>this</strong>, and not <strong>that OR this</strong> and <strong>that</strong> (and not <strong>those</strong>)) I think that these puzzles help me reason more clearly to simplify my code.</p>
<p>They also help me technically plan out my approach to a problem, from the very beginning to the eventual solution. </p>
<p>I start from a basic set of requirements and no idea how all they fit together. But as I move along, I can go through a process of fact finding, checking edge cases, verifying/testing my logic against the requirements and finally submitting my work. All of these steps translate exactly to software development.  </p>
<p>Whenever you have a complicated set of states you are confused by, draw a basic truth table. Or however you want to represent the problem. Breaking it down into smaller and smaller problems will allow you to solve almost anything.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>I hope this has been an enjoyable brain teaser, and that you solved as much or as little as was fun for you.</p>
<p>I share my writing on <a target="_blank" href="https://twitter.com/kealanparr">Twitter</a> if you enjoyed this article and want to see more.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Logical Fallacies – Definition and Fallacy Examples ]]>
                </title>
                <description>
                    <![CDATA[ When you're debating someone, you want to use all the resources at your disposal to convince them you're right. And that's great – but you should be careful that you don't end up using a logical fallacy to help you make your point. What is a Logical ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/logical-fallacies-definition-fallacy-examples/</link>
                <guid isPermaLink="false">66b1fa5501079c4f2679de8e</guid>
                
                    <category>
                        <![CDATA[ logic ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Philosophy ]]>
                    </category>
                
                    <category>
                        <![CDATA[ politics ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Abigail Rennemeyer ]]>
                </dc:creator>
                <pubDate>Wed, 23 Jun 2021 04:19:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/05/fallacies-cover-image.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you're debating someone, you want to use all the resources at your disposal to convince them you're right.</p>
<p>And that's great – but you should be careful that you don't end up using a logical fallacy to help you make your point.</p>
<h2 id="heading-what-is-a-logical-fallacy">What is a Logical Fallacy?</h2>
<p>A logical fallacy is an error in reasoning that makes your argument less effective and convincing. And you want to be able to spot these fallacies in other people's arguments (and your own) so you can call them out or fix your own strategy.</p>
<p>There are two major types of logical fallacies, formal and informal.</p>
<p>In formal fallacies, there's a problem with <strong>how</strong> you structure your argument, and how you're making your points. You might be speaking the truth, but the logic breaks down because of the way you're putting your arguments together.</p>
<p>In informal fallacies, there's a problem with <strong>what</strong> you're saying, and the information might be incorrect or misleading. </p>
<p>In this article, we'll focus on these informal fallacies as they can be pretty common in everyday debate. And keep in mind that we're not talking about the effectiveness or persuasiveness of your argument, here – after all, fallacious arguments can be very persuasive.</p>
<p>Instead, it's all about giving you the tools to identify these weak arguments so you don't make these mistakes in your reasoning.</p>
<h2 id="heading-list-of-logical-fallacies-with-examples">List of Logical Fallacies with Examples</h2>
<p>In this article, we'll look at the most common informal fallacies so you can learn to identify them and avoid them.</p>
<h2 id="heading-the-sunk-cost-fallacy-definition-and-example">The Sunk Cost Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/sunk-cost-fallacy.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Have you ever finished a task (that you really didn't want to complete) simply because you'd put so much time and effort in already? You probably felt like you didn't want all that hard work to go to waste, or to be for nothing.</p>
<p>You were likely falling prey to the sunk cost fallacy. It states that it's actually better to abandon a project that's going nowhere (at any point) rather than waste any more time, energy, and resources trying to finish it for the sole purpose of finishing it.</p>
<p>The reason for this might seem counterintuitive, but think about it: rather than spend another minute of your precious time doing something that isn't going anywhere, it's better to switch gears ASAP (before you spend any <em>more</em> time) and start putting your energy into something productive.</p>
<h3 id="heading-example-of-a-sunk-cost-fallacy">Example of a Sunk Cost Fallacy</h3>
<p>Let's say that you've decided to write a book. You spend hours and hours doing research, making an outline, and writing the first 10 chapters. You've put months if not years of your life into writing this book.</p>
<p>But then perhaps your interests change, or you no longer wish to be an author. You might think you should finish the book because you're <em>so close</em> or because you've already spent so much time and energy on it. </p>
<p>Instead, though, you should leave that project behind and focus on what's ahead. Maybe you're trying to get a new job, or learn a new skill, or move to a new city. Any of these current and relevant initiatives would suffer if you continued to work on your unsuccessful book project.</p>
<p>So how do you distinguish between this sunk cost fallacy and persevering until you finish something difficult? Well, it helps to think about whether the experience will benefit you in the long run – in which case, it would be helpful to see it through. </p>
<p>For example, let's say you've done three years of a four year degree program at a college or university. But your interests have changed, and you want to pursue something that doesn't require that degree. </p>
<p>Still, it might make sense to finish the program, as a college degree typically only helps you in future career moves – not to mention the life experience you'll gain in the process.</p>
<h2 id="heading-the-ad-hominem-fallacy-definition-and-example">The Ad Hominem Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/ad-hominem-fallacy.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Ad hominem means "against the person" in Latin. So the ad hominem fallacy happens when you attack a person's character, appearance, personality, or other irrelevant aspects in an argument instead of attacking what they're saying.</p>
<p>These types of attacks are fallacious because they're not relevant to the argument, and so they distract from the point at hand. It doesn't really matter if you think your mom is being a jerk – she's still right that you shouldn't speed while driving.</p>
<h3 id="heading-example-of-an-ad-hominem-fallacy">Example of an Ad Hominem Fallacy</h3>
<p>Many people associate ad hominem fallacies with political debates. Unfortunately, some candidates don't seem to be able to help themselves. </p>
<p>What if Candidate A said that you shouldn't trust Candidate B because Candidate B doesn't dress well? There's no established link (that I know of!) connecting a "good dresser" with trustworthiness or good political decision-making, so this would be an ad hominem fallacy.</p>
<p>Or what about when Candidate A insults Candidate B for being too nerdy, or not cool enough? These qualities, first of all, are subjective, and second, they shouldn't affect Candidate B's ability to govern effectively.</p>
<p>On the other hand, sometimes people just deliver insults that aren't actually logical fallacies because they aren't part of the argument. For example, if you were to say that all New Yorkers are rude and unfriendly (but you aren't trying to make a point), that's just an (untrue) insult and not a fallacy.</p>
<p>So when you're debating someone, leave their personal characteristics out of it unless they're relevant to your point.</p>
<h2 id="heading-the-straw-man-fallacy-definition-and-example">The Straw Man Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/strawman-fallacy.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When you hear the term "straw man", what comes to mind? Probably a figure of a person made of straw, like a scarecrow, or something else insubstantial. That straw figure isn't too solid, and you could just knock it over with a little push or a strong gust of wind.</p>
<p>The same holds true for straw man fallacies – they represent weaker arguments that are oversimplified or that distract from the main point the debater is trying to make. </p>
<p>So instead of responding to someone with a well-reasoned, to-the-point counterargument, someone using a straw man might reframe that person's argument in a vastly oversimplified way, or might latch on to an irrelevant point that's tangentially related and go after that. Basically, they create a "straw man" in place of a real argument.</p>
<h3 id="heading-example-of-a-straw-man-fallacy">Example of a Straw Man Fallacy</h3>
<p>Perhaps you're discussing education with someone who believes that for-profit colleges are harmful to the broader educational system because they take advantage of their students, don't provide them high-quality education, and waste students' money.</p>
<p>Instead of responding with appropriate counterpoints (such as concrete examples of for-profit colleges who benefit their students), you try to undermine the person's argument by saying "See, they're against higher education and don't think people should go to college!"</p>
<p>In fact, the person has a much more nuanced claim, but you've ignored it and constructed a vague straw man fallacy in response.</p>
<p>Or maybe you're trying to figure out a solution to the number of people living without homes in your area. You might suggest setting up temporary (or permanent) tiny homes for houseless individuals, allocating resources for trash cleanup, and providing medical care during the pandemic.</p>
<p>Your opponent, however, might misconstrue your argument and insist that you're trying to welcome the homeless community to your area by providing so many benefits for them.</p>
<h2 id="heading-the-false-dilemma-fallacy-aka-the-false-dichotomy-fallacy-definition-and-example">The False Dilemma Fallacy (AKA The False Dichotomy Fallacy) – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/false-dichotomy-pic-2.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Have you ever argued with someone and they only give you two options when you feel like there are many more? Chances are they were falling into the trap of the false dichotomy.</p>
<p>Using a false dichotomy or false dilemma in an argument means that you oversimplify your argument or only focus on two outcomes when in fact there are other reasonable possibilities. </p>
<p>This strategy tries to hide important facts and considerations and tries to trick your opponent into thinking the argument is more cut and dry or simpler than it really is.</p>
<h3 id="heading-example-of-a-false-dilemma-fallacy">Example of a False Dilemma Fallacy</h3>
<p>Let's say that you're still working on finding homes for houseless people in your community. You might suggest a range of housing options, such as tiny houses, community living, repurposing empty apartment buildings, and so on. </p>
<p>You could also offer to relocate people who wished to leave your area, or you could help them find jobs so they could afford their own home eventually.</p>
<p>Someone opposed to your efforts might say that houseless people either need to get a job so they can afford their own place or leave town. And they wouldn't offer any of the other options you explored. </p>
<p>To someone uninformed about the crisis of homelessness in your area, those two options might sound reasonable. But to someone who had studied the issue extensively, it would be clear that those extremes weren't the only options.</p>
<p>How about another example?</p>
<p>Maybe you're at a political debate and one of the candidates asserts that you're either a Democrat or you're a Republican in an effort to make some point.</p>
<p>In reality, though, this likely wouldn't be the case. Certain people in attendance could be Libertarians, for example – but the politician didn't include that as an option.</p>
<p>So keep in mind, when you're making an argument, that there are likely many nuances that relate to your point. Don't ignore them – simply take them into account and build them into your argument.</p>
<p>Do keep in mind, though, that some arguments really only do have two viable options – so they wouldn't represent false dichotomies. For example, if a General says "Either you're with us or you're against us" during a war, those are the two main options.</p>
<h2 id="heading-the-slippery-slope-fallacy-definition-and-example">The Slippery Slope Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/slippery-slope.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The slippery slope fallacy refers to arguments that get increasingly dramatic and out of hand very quickly. Especially when the ever-more-dramatic conclusions aren't realistic or likely to happen.</p>
<p>These types of arguments are often made when someone wants to emphasize how drastically bad an outcome would be.</p>
<p>Perhaps a better name for this fallacy, though, would be the Domino Effect – one thing <em>might</em> lead to another which <em>might</em> lead to another which might...and so on. The problem with these assumptions is that they're all hypothetical, which makes your overall claim very weak.</p>
<h3 id="heading-example-of-a-slippery-slope-fallacy">Example of a Slippery Slope Fallacy</h3>
<p>Perhaps your teenager wants to buy themselves a truck. They've been saving up, and they have the money. But you don't want them to drive a truck, for any number of reasons – perhaps you're worried about gas mileage, or parking in a city, or that they'll take it off-roading and get hurt.</p>
<p>Now, these are all fairly reasonable arguments as to why you wouldn't want your kid driving a truck, and they could easily result from that purchase.</p>
<p>But what if, instead of these sensible arguments, you let your emotions get away with you and instead said "You can't get a truck because then all your friends will want trucks and their whole families will then get trucks which they'll start driving all over the place and over-polluting the earth!"</p>
<p>You can see how that escalated quickly, right? And even though the arguer has a point about emissions in general here, it's probably not a realistic outcome of this situation (and it's probably not an effective argument to use to convince your teen not to buy a truck).</p>
<h2 id="heading-the-circular-reasoning-fallacy-definition-and-example">The Circular Reasoning Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/circular-reasoning.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Have you ever noticed someone arguing in a way that they seem to go around in a circle? It might seem like they're making an argument, but they'll use their conclusion to justify their argument, and their argument to justify their conclusion.</p>
<p>If this sounds confusing, that's because it is. When someone says something like "This tee-shirt is wet because it's covered in water," they're making a fallacious argument. In fact, the tee-shirt is wet <em>because you fell in a lake</em>, for example.</p>
<p>In this case, someone saying something's wet because it's covered in water is just stating the obvious. They're not offering an explanation for <em>why</em> it's that way.</p>
<p>You can often recognize a circular argument when the conclusion – the thing the person is arguing in favor of (or against) – is also one of the premises (or arguments) they're using to justify their assertion (it's wet because of water, which is wet). In other words, if this is true because that is true, that is true because this is true.</p>
<h3 id="heading-example-of-a-circular-reasoning-fallacy">Example of a Circular Reasoning Fallacy</h3>
<p>So here's another example: you say that your friend Jessie lies all the time, and you know this because they never tell the truth. But your argument (that Jessie lies all the time) and your premise (because they never tell the truth) are the same thing. That means that this is a circular argument.</p>
<p>Here's another way to think about it: if your argument's premises assume that your conclusion is true right from the beginning, rather than proving or finding that it's true, you're arguing in a circle. Just remember: if your argument is defined in terms of itself, it is probably fallacious.</p>
<p>And if you want to know why it's sometimes called "Begging the Question," you can <a target="_blank" href="https://en.wikipedia.org/wiki/Begging_the_question">read all about it here</a>. (Hint: it's a mistranslation of 16th century Latin that was actually a mistranslation of the ancient Greek phrase...fascinating.)</p>
<h2 id="heading-the-equivocation-fallacy-definition-and-example">The Equivocation Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/equivocation-fallacy.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Equivocation means that you're taking a word or phrase and changing its meaning slightly so that it means something else. Or you're using one word or phrase instead of another to hide the true meaning of what you're saying.</p>
<p>In other words, you're being ambiguous with your language. If something is ambiguous, it means that you can interpret it in more than one way or that it has two meanings. This is exactly what happens in an equivocation fallacy.</p>
<p>The word "equivocation" comes from the Latin for "equal voice" – meaning that it appears that what you're saying means one thing but it really means or can also mean something else. </p>
<p>The important thing to remember about equivocation fallacies is that they attempt to <strong>deceive</strong> in some way. </p>
<p>You might jokingly use ambiguity in a story, play, or playful conversation – but you're not really trying to convince your listener of something serious (or it's clear that you're being tricky or silly).</p>
<p>But when you use equivocation in a serious debate, political campaign, advertisement, or something similar, that's when it's more malicious and fallacious.</p>
<h3 id="heading-example-of-an-equivocation-fallacy">Example of an Equivocation Fallacy</h3>
<p>So how do you tell the difference? Be mindful of the setting in which you use ambiguous language, or you see it being used.</p>
<p>Here's a simple example: "Nine out of ten dentists recommend Colgate toothpaste." First of all, what does "recommend" mean here? This could be misleading – do they really specifically recommend Colgate, or do they just recommend that you brush your teeth in general? </p>
<p>How about another example? What if you break up with someone, and they ask you never to drive by their house again. So you walk by – but you justify it by saying that you didn't drive by. You walked.</p>
<p>Clearly your ex meant that they didn't want you going by their house in any way, but you used the ambiguity of the situation to tweak their words and do it anyway.</p>
<h2 id="heading-the-post-hoc-fallacy-definition-and-example">The Post Hoc Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/post-hoc-fallacy.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You might have heard the phrase "post hoc ergo propter hoc" before, even if you've never studied Latin.</p>
<p>This Latin phrase translates to "After this, therefore because of this." Now that might sound like a jumble of conjunctions and such, but it basically means that if event B happened after event A, that must mean that event A caused event B. </p>
<p>Post hoc ergo propter hoc → (B is) After this (A), therefore (B is) because of this (A). </p>
<p>This fallacy says that because one thing happened after another, it means that the first thing <em>caused</em> the second thing happen. The argument is a fallacy when someone asserts something based purely on the order that things happened. This means they're not taking into account other factors that affected or caused the event to happen.</p>
<p>If this sounds a bit familiar to you, it means you might have thought about <a target="_blank" href="https://www.freecodecamp.org/news/why-correlation-does-not-imply-causation-the-meaning-of-this-common-saying-in-statistics/">correlation vs causation</a> before. The post hoc fallacy is related, but is more focused on the order of events (and their relationship).</p>
<h3 id="heading-example-of-a-post-hoc-fallacy">Example of a Post Hoc Fallacy</h3>
<p>Let's look at an example to help decipher what's going on in this type of fallacious argument.</p>
<p>Maybe there was an earthquake during which a building fell down. That's a pretty clear example of causality – the earthquake (event A) caused the building to fall down (event B).</p>
<p>But what if, after that same earthquake, a lot of people moved away from the city? Now, some of them might have moved because the earthquake was the last straw. But many might have fled because of rising housing costs, pollution, over-crowding, poor infrastructure, poor schools, or a bunch of other factors. </p>
<p>In other words, the earthquake likely wasn't the only direct cause of people moving away.</p>
<p>So anyone who argued "Look, people are moving out of the city because of the earthquake!" and didn't account for all these other likely causes was making a fallacious argument.</p>
<p>Here's another example: perhaps you're searching for a job, and you're not having any luck. But then someone gives you a good luck charm, and after a few more applications, you get a job.</p>
<p>You might be tempted to think that the good luck charm got you the job. But what's more probable is that you put a lot of effort into your applications, you studied really hard for your interviews, and you found your perfect company fit.</p>
<h2 id="heading-the-appeal-to-authority-fallacy-aka-argumentum-ad-verecundium-fallacy-definition-and-example">The Appeal to Authority Fallacy (AKA Argumentum ad Verecundium Fallacy) – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/appeal-to-authority.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When you're gathering evidence to support your conclusion, you'll likely want to cite some experts. They've done research on the subject and know a lot about it, so it makes sense to use their knowledge and opinions to support your own arguments.</p>
<p>But be careful – if you don't use those expert's information correctly, or if you assume they're always right because they're experts, you could be falling prey to the appeal to authority fallacy.</p>
<p>An appeal to authority fallacy is easy to commit, but can be hard to recognize. This is because of the weight we all give to "authorities" in various subjects. </p>
<p>When you're engaging in an appeal to authority fallacy, you're likely either misusing someone's authority, citing an irrelevant authority, or citing a poor authority. </p>
<p>Let's see what these look like with some examples.</p>
<h3 id="heading-example-of-an-appeal-to-authority-fallacy">Example of an Appeal to Authority Fallacy</h3>
<p>Let's say your mom's a lawyer and you seek her advice about a particular legal problem you have. If she practices that type of law and has experience with the problem you're having, you can likely cite her authoritative opinion with confidence.</p>
<p>But if you're arguing with your mom about the best way to save the sea turtles, and she asserts that she knows best because she's an intelligent person, she's using her own authority in a fallacious way (and with little to no justification). </p>
<p>Here's another example. Perhaps you watch a lot of Greenbay Packers football, and Aaron Rogers is your favorite quarterback. You happen to see a State Farm insurance commercial where Aaron endorses State Farm's services. You might think, "Well, I like Aaron Rogers, and he recommends State Farm, so it must be great insurance!"</p>
<p>While State Farm might be great insurance, Aaron Rogers doesn't have the authority to say so. He's an authority on being a great quarterback, but not on the quality or efficacy of insurance. So this is an example of an irrelevant appeal to authority. </p>
<p>So, when you're searching for evidence to back your claim, just remember – authorities aren't the only sources you should cite. </p>
<p>And you shouldn't just expect people to trust what those experts say with no evidence. After all, even the experts can be wrong, and just because they know a lot about one thing doesn't mean they know a lot about everything.</p>
<h2 id="heading-the-appeal-to-ignorance-fallacy-definition-and-example">The Appeal to Ignorance Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/pexels-mathias-pr-reding-5662219.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>No one knows everything – it's just a fact of being human. We're all still learning, and while some might know more than others, we'll all be ignorant about certain things.</p>
<p>With that in mind, it's pretty easy to see why the appeal to ignorance fallacy is so common and so useless.</p>
<p>When you say something like "Well, no one's ever seen Nessie (the Loch Ness Monster) before, so they can't prove that she's real", you're making an appeal to ignorance. Why? Because no one knows whether she exists or not – because they've never seen her!</p>
<p>But the clearest way you can tell this is an appeal to ignorance fallacy is that you can turn it right around, and it still seems to make sense: "Well, no one's ever seen Nessie before, so they can't prove that she's not real!" </p>
<p>Either way, in both these claims, you're making an assertion <strong>based on something no one knows</strong> (the ignorance bit). Because no one knows it, you shouldn't use it in an argument.</p>
<h3 id="heading-example-of-an-appeal-to-ignorance-fallacy">Example of an Appeal to Ignorance Fallacy</h3>
<p>Let's look at another example of an appeal to ignorance fallacy in action.</p>
<p>Perhaps you're an archaeologist who's studying an ancient civilization that lived around 2000 years ago. You study any remaining stone structures, pottery, tools, jewelry, and anything else they left behind.</p>
<p>You try to piece together what life would've looked like for these people based on their artifacts, where they lived, nearby societies, and so on. But you have no written evidence that tells you anything more. No one has found any inscriptions, written documents, or anything else with writing on it.</p>
<p>It would be tempting to assert that, since no one has ever found any evidence of writing, this society didn't have a written language. "We've never found documents or inscriptions, so they must not have written their language down."</p>
<p>But you could also assert that, even though no one has found those documents <strong>yet</strong>, they still might be out there and just haven't been excavated and discovered yet.</p>
<p>This argument is an appeal to ignorance, because you don't know something/haven't seen any evidence of something, but you're using it to support your argument (that the society doesn't have a written language) all the same. </p>
<h2 id="heading-the-appeal-to-popular-opinion-fallacy-aka-bandwagon-fallacy-or-ad-populum-fallacy-definition-and-example">The Appeal to Popular Opinion Fallacy (AKA Bandwagon Fallacy or Ad Populum Fallacy) – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/bandwagon.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Have you ever heard the expression "jumping on the bandwagon"? It refers to someone changing their opinion or developing an opinion just because a bunch of people hold that same opinion.</p>
<p>There's not necessarily good evidence for that opinion, but people hold it anyway – maybe because it's been believed for a long time, or just because of the sheer number of people who believe it. But even though many people believe this thing, it may be factually incorrect or misleading.</p>
<p>This is a form of the appeal to popular opinion fallacy. You argue that something is true, good, or right just because a large number of people (or some popular or influential person or people) are doing it or believe it.</p>
<p>What's wrong with that? If everybody's doing it, it must be good – right? Well, not necessarily. People aren't always completely rational and don't always think things through. Think of the term "mob mentality". What does that conjure up? Probably a bunch of people causing chaos – in other words, not a good thing.</p>
<p>So before you say something like "Well everyone believes this, so it must be true", think again. Because this isn't a case of "strength in numbers" – an ad populum fallacy results from a lot of people believing incorrect or misleading information.</p>
<h3 id="heading-example-of-an-appeal-to-popular-opinion-fallacy">Example of an Appeal to Popular Opinion Fallacy</h3>
<p>What if your young teenager comes to you and wants to get a tattoo. They argue that all their high school friends are doing it because some celebrity just got this new tattoo.</p>
<p>Now, whatever your feelings about tattoos, this is a logical fallacy. Just because everyone's getting this tattoo doesn't mean it's the right choice for your kid. Maybe they haven't thought it through, or maybe they can't handle serious pain/needles, or maybe they will change their mind in a few years and regret such a permanent choice.</p>
<p>Also, everyone has different reasons for getting tattoos. Some do it to commemorate someone or something, some do it for the beauty of the art, some do it while intoxicated on vacation, and so on. But if a group of young teenagers is getting a tattoo on a whim to copy a celebrity, perhaps that's something you want your kid to think about more carefully.</p>
<p>So your kid arguing that "all my friends are doing it, so it's cool" doesn't take that into account. They'd need to think about getting a tattoo for their own reasons, and justify it to you that way.</p>
<p>Here's another example: you're FaceTiming with your family, and it's an election year. Most of your family belongs to one political party, but you belong to another. </p>
<p>Your mom starts trying to convince you to vote like they do – "The whole family votes this way! And we've been voting this way forever! Come on, you should be like your family and support the same candidate/things we do."</p>
<p>While it's understandable that your mom would want your political beliefs to align with hers, she's making a fallacious argument here. Just because they've always voted that way doesn't make it right.</p>
<p>She shouldn't say you should vote like she does because "that's what the family's always done/it's what they all do now". She should point out the benefits of her candidate, how they could help you out, why their policies are fair, and so on – and then let you decide for yourself.</p>
<h2 id="heading-the-hasty-generalization-fallacy-definition-and-example">The Hasty Generalization Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/grilling.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>People make generalizations all the time (that, right there, was a generalization!). And sometimes this is ok. If you're just stating something that's generally true, like "I like to cook" or "Puppies are cute", there's typically no harm in that. </p>
<p>The problem arises, though, when someone uses a generalization a bit too zealously in an argument without sufficient evidence. These types of "hasty" generalizations can fall into stereotyping, racism, falsehood, exaggeration, and more. </p>
<p>Often someone makes such a generalization when they're basing their opinion or argument off of the behavior or characteristics of just a few members of a group. This often means they're not taking the behavior of the whole group into consideration.</p>
<p>So why are these generalizations bad? Aside from lacking evidence and being based on problematic premises, people often assert hasty generalizations as if they were 100% true all the time. Which, of course, very few likely are.</p>
<p>If you want to avoid making hasty generalizations, you can use certain qualifiers when you make a generalization – like "Sometimes", "Often", "We often see", or "It may be the case that...". Those types of words and phrases let your listener know that you're not arguing that this thing is true across the board for everyone. It's just a general trend you've noticed.</p>
<h3 id="heading-example-of-a-hasty-generalization-fallacy">Example of a Hasty Generalization Fallacy</h3>
<p>Hasty generalizations are quite common, as people use generalizations all the time in regular conversation. And again, many generalizations don't hurt anyone. But let's look at some examples of bad generalizations.</p>
<p>If you say "People in the southern part of the US are so conservative and close-minded. I really can't stand how all they care about is football and BBQ", you're using a hasty generalization (a couple, actually). </p>
<p>While it's true that some people in the south have these characteristics, it's not true for everyone living in that region. And by making those assertions, you're perpetuating stereotypes that are likely overblown and miss a lot of nuance about southern American's characters and beliefs.</p>
<p>Here's another example: let's say you're having a fight with your significant other and you say, "You always pick fights with me!", you're likely exaggerating and making a hasty generalization. Unless it's literally true that they are always the one to start the fight, you're probably getting carried away in the heat of the moment.</p>
<p>One way to save yourself from making a hasty generalization in this case would be to say something like "You pick fights with me a lot" or "You often pick fights with me."</p>
<h2 id="heading-the-tu-quoque-fallacy-aka-appeal-to-hypocrisy-fallacy-definition-and-example">The Tu Quoque Fallacy (AKA Appeal to Hypocrisy Fallacy) – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/tu-quoque.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Tu quoque in Latin means "You, too". And when you attempt to distract from your own guilt by calling out someone else's similar guilt, you're committing this fallacy.</p>
<p>The name makes sense – it's like you're saying "Well I may have done this, but you did it, too!" Now, think about that. Just because someone else did something similar to (or the same as) what you did, it doesn't make you any less guilty. You've still committed whatever crime or done whatever bad thing you've done.</p>
<p>This is also called an "appeal to hypocrisy" fallacy, because the person making the argument (let's call them Person A) often calls out the fact that someone else (Person B) did something similar to what they did. Person A argues that they may have messed up, but Person B did the same thing so should be punished. Person A is being a hypocrite because they're trying to escape the blame they'd like to assign to Person B.</p>
<p>It's tempting to use this type of argument, because people are always looking to shift the blame from themselves to others. It's especially enticing when that other person is not blameless and therefore seems to deserve some share of the guilt.</p>
<p>But this isn't an effective argument strategy because, while distracting, a tu quoque argument doesn't actually prove you innocent. It just draws attention (falsely) away from the issue at hand, which is your misdeed.</p>
<p>One thing to remember about tu quoque fallacies is that the information the person making the argument cites is typically irrelevant to the case at hand. Just because Person B is guilty also, doesn't mean Person A is any less guilty. So that accusation that Person A makes is irrelevant to their case.</p>
<h3 id="heading-example-of-a-tu-quoque-fallacy">Example of a Tu Quoque Fallacy</h3>
<p>Let's go back to our teenager. Perhaps they've been caught skipping school, and their parents want to ground them for a week. The teenager might argue, "Yeah I skipped third and fourth periods, but Marta did, too!"</p>
<p>While it's not great that Marta skipped class as well, it doesn't really make that teen any less guilty of skipping school. They just knew someone who did the same thing, and are trying to justify what they did by bringing up Marta's transgression as well. But it doesn't mean that they skipped any less school.</p>
<p>Here's another example: perhaps your friend caught you cheating on a test, and threatened to turn you into the teacher. But you saw them cheat in another class last year, so you say "I may have cheated today, but you cheated on that math test last year, too!"</p>
<p>Again, their cheating a year ago doesn't make you any less guilty right now. While it might feel good to say, "You did that, too, so how could you think I should be punished for it!", it's not really a strong or relevant argument to make.</p>
<p>Instead of resorting to this type of argument, make sure you take responsibility for your actions and keep your points relevant to the issue at hand. Don't think you can get away with something just by calling out someone else's hypocrisy. It's likely not going to help your case.</p>
<h2 id="heading-the-loaded-question-fallacy-example-and-definition">The Loaded Question Fallacy – Example and Definition</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/loaded-question.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When you ask a question that intends to reinforce your position and undermine someone else's, you could be asking a loaded question. These questions are helpful to you but harmful to the person you're asking, and may skew the opinion of anyone listening in your favor, perhaps unfairly.</p>
<p>Instead of asking a straightforward question that attempts to get more or new information, a loaded question often includes an accusation (or a confirmation of an accusation) – an oft-quoted example is "Are you still beating your wife?"</p>
<p>In this question, you're referencing an accusation – that the person beat their wife – without directly accusing them of doing it currently. But by including it in the question, you're turning listeners' minds to the fact that this person did, at one point, beat their wife. So either way, they'll appear guilty.</p>
<h3 id="heading-example-of-a-loaded-question-fallacy">Example of a Loaded Question Fallacy</h3>
<p>Let's look at some more examples of loaded questions, and why they're fallacies.</p>
<p>Perhaps you're at a rally in support of clean energy, and a rep from Exxon is there. If you're not old enough to remember, <a target="_blank" href="https://en.wikipedia.org/wiki/Exxon_Valdez_oil_spill">Exxon had a horrific oil spill</a> in Alaska in 1989 that devastated 1300 miles of coastline and released over 10 million gallons of oil into the ocean.</p>
<p>You might call out that rep and loudly ask them if their company is still polluting the world's pristine oceans and killing millions of sea creatures. </p>
<p>Whatever your feelings about Exxon or environmental justice, it's not fair to set the company up like that for those listening. Your question is heavily loaded, and doesn't give them a shot at convincing others of their current position, whatever it might be. You're making your argument by essentially biasing the crowd against them from the start.</p>
<p>Here's another example: what if a company hires formerly incarcerated people, and you find out that one of them was a bank robber. If you asked their employer "You're really gonna let a thief handle your products?" you're creating a negative bias against them. </p>
<p>It's not necessary to refer to them as a thief or allude to their past as a bank robber. By doing so, you're only creating prejudicial feelings against them that may not be relevant or meaningful at this point in time.</p>
<p>So just remember – when you're asking questions to try to prove your point, keep them relevant, unbiased, and focused on the issue at hand.</p>
<h2 id="heading-the-red-herring-fallacy-definition-and-example">The Red Herring Fallacy – Definition and Example</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/Redherring.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You might wonder where the term "red herring" comes from. It's a bit of an odd name for a fallacy, don't you think?</p>
<p>Well, there has been some <a target="_blank" href="https://en.wikipedia.org/wiki/Red_herring">debate about this in the past</a> but most sources agree that a red herring signifies a distraction or something meant to mislead someone. </p>
<p>Fun fact before we continue: there's not actually a species of herring called a red herring. A "red herring" refers to a herring that's been brined and smoked until it becomes extremely pungent and turns a bright red color.</p>
<p>So these red herrings were used as training aids for animals because of their strong smell (to attempt to lead them in a certain direction).</p>
<p>Anyway, back to our fallacy: if you make an argument with the intention of distracting from the real issue at hand, it might be a red herring. Also, if you drop some seemingly related bit of info into a conversation or debate that leads your listener down the wrong path, that's also a red herring.</p>
<p>Ultimately, a red herring argument distracts or leads your listener away from the crux of the issue so that they get off course or off topic.</p>
<h3 id="heading-example-of-a-red-herring-fallacy">Example of a Red Herring Fallacy</h3>
<p>Remember, a red herring basically a diversionary tactic in an argument. It's meant to lead the listener away from the main point of the conversation.</p>
<p>Suppose you're arguing with someone who is in favor of a dam that's being constructed in a beautiful river. You bring up the environmental impact that said dam will have, and how devastating it'll be to the surrounding natural habitat.</p>
<p>Your opponent might say something like "Yes it will destroy the habitat for many fish and other river animals, but if we don't build the dam it'll take jobs away from so many people who would've worked on it."</p>
<p>Now, this person has just used a red herring fallacy to try to distract from the environmental impact of such a dam. Instead of arguing for the benefits of the dam itself, and arguing against the environmental impact, they're dropping in a red herring – the potential impact on the workers who would've been hired to build the dam.</p>
<p>While that itself is a whole separate issue, it doesn't deal with or respond to the issue at hand, which is what happens to the natural environment when the dam goes in.</p>
<h2 id="heading-how-to-avoid-logical-fallacies-in-your-arguments">How to Avoid Logical Fallacies in Your Arguments</h2>
<p>We've just discussed a whole bunch of logical fallacies, and you might be thinking – how can I make any arguments at all without saying something fallacious?</p>
<p>It's not always easy, as some of these fallacies are very tempting and easy to fall into. But as long as you stick to the point, don't try to deceive your listener, cite relevant evidence from relevant sources, and avoid any derogatory or misleading language, you should be ok.</p>
<p>Good luck, and happy debating!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Common Logic Puzzles – The Knights and Knaves, Monty Hall, and Dining Philosophers Problems Explained ]]>
                </title>
                <description>
                    <![CDATA[ While not strictly related to programming, logic puzzles are a good warm up to your next coding session. You may encounter a logic puzzle in your next technical interview as a way to judge your problem solving skills, so it's worth being prepared. In... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/common-logic-puzzles-the-knights-and-knaves-monty-hall-and-dining-philosophers-problems-explained/</link>
                <guid isPermaLink="false">66c347a04f1fc448a3678fd0</guid>
                
                    <category>
                        <![CDATA[ logic ]]>
                    </category>
                
                    <category>
                        <![CDATA[ puzzles ]]>
                    </category>
                
                    <category>
                        <![CDATA[ toothbrush ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 01 Feb 2020 00:00:00 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c9d11740569d1a4ca35b6.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>While not strictly related to programming, logic puzzles are a good warm up to your next coding session. You may encounter a logic puzzle in your next technical interview as a way to judge your problem solving skills, so it's worth being prepared.</p>
<p>In this article, we've collected a few famous logic puzzles and their solutions. Can you solve them without peeking at the answer?</p>
<h2 id="heading-knights-and-knaves">Knights and Knaves</h2>
<p>For this logic puzzle, imagine there are two types of people, knights and knaves. Knights only tell the truth, while Knaves only tell lies.</p>
<p>There are many variations of this puzzle, but most involve asking a question to figure out who is the knight and who is the knave.</p>
<h3 id="heading-red-and-blue">Red and Blue</h3>
<p>There are two people standing in front of you, Red and Blue. Blue says, "We are both knaves." Who is really the knight and who is the knave?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/photo-1556549957-f41c6fcc4210-4.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>Solution</strong><br>It's impossible for Blue to be the knight. If Blue was a knight, the statement, "We are both knaves," would actually be a lie. Therefore, Blue is a knave as his statement is a lie, and Red must be a knight.</p>
<h3 id="heading-two-paths">Two Paths</h3>
<p>You arrive at a fork in the road and need to choose the correct path that leads to your destination. There are two people standing at the fork, and you know that one must be a knight and the other must be a knave. </p>
<p>What single question could you ask to one of the people to determine the correct path, A or B?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/photo-1519401706-5cf17f6e70de.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>Solution</strong><br>The question you can ask either person is, "What path would the other person tell me is the correct one?" The answer will always be the wrong path to take, and you can safely take the other path.  </p>
<p>Imagine the correct path is A.   </p>
<p>If you ask directly, "Which is the correct path?" the knave will say B is correct while the knight will say A.  </p>
<p>However, when asked which path the <em>other</em> person would say is correct, the knave will lie and say that the knight would tell you path B is correct. Meanwhile, the knight will tell the truth about the knave's answer, and say that the knave will tell you that path B is the correct one.  </p>
<p>In both cases  you know that then answer, path B, is actually a lie, so you should take the other path.</p>
<h2 id="heading-the-monty-hall-problem">The Monty Hall Problem</h2>
<p>The Monty Hall Problem is a riddle on probability named after the host of the 70’s game show it’s based on, <em>Let’s Make a Deal</em>. This particular problem is a <a target="_blank" href="https://en.wikipedia.org/wiki/Paradox">veridical paradox</a>, which means that there is a solution that seems counter-intuitive, yet proven to be true.</p>
<p>Imagine you are on a game show and there are 3 doors, each with a different prize behind them. Behind one of the three doors is a car. Behind the other two doors there are goats. </p>
<p>You must choose one of the 3 doors to select as your prize. </p>
<p>Say you decide to open Door 1. The host, who knows where the car is, opens a different door, Door 2, which reveals a goat. He then asks if you would like to open Door 3 instead.</p>
<p>Should you choose Door 3 over your original choice? Does it even matter?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/zachary-anderson-ceYJ1HKt9Rk-unsplash.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>Solution</strong><br>It turns out that your choice really does matter, and it is actually to your benefit to choose Door 3 instead of Door 1. Here's why.</p>
<p>When you chose Door 1 from the 3 closed doors, you had a 1 out of 3 chance that you picked the right one. Both Door 2 and Door 3 also have a 1 out of the 3 chance of having a car behind it. </p>
<p>Another way to think about it is that Doors 2 and 3 have a 2 out of 3 chance of having a car behind it <em>combined</em>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/8EsVvZk-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>But when the host opens Door 2 and reveals the goat, you suddenly have more information about the problem.</p>
<p>Remember that Doors 2 and 3 have a combined probability hiding the car 2/3rds of the time. When Door 2 was opened you know that there was no car behind it.</p>
<p>But this reveal does not change the combined probability of the two doors. That’s the key takeaway here!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/V2JzAka-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Since you know that Door 2 has a 0/3 chance of hiding the car, you can now say that there's a 2/3 chance that the car is behind Door 3. Door 1 remains unchanged – there's only a 1/3 the car is behind it.</p>
<p>So if you decide to switch, you go from roughly a 33.33% chance to a 66.67% chance of finding the car. In other words, you are doubling your chances of success by opening Door 3 instead!</p>
<p>Yes, it is possible that Door 1 was hiding all along and host tricked you. That doesn’t matter. You are gambling by taking the deal, but you’re gambling smart. You should make the best decision with the information you’re given and let the dice roll. </p>
<p>In the long run, you'd perform better by switching than a contestant who decides to go with their first choice. Though it's not immediately obvious, the host is actually doing you a favor by offering you a better deal.</p>
<h2 id="heading-the-dining-philosophers-problem"><strong>The Dining Philosophers Problem</strong></h2>
<p>The dining philosophers problem is a classic example in computer science to illustrate issues with synchronization. It was originally created by Edsger Dijkstra in 1965, who presented it to his students as a handful of computers competing for access to shared tape drives.</p>
<p>Imagine five silent philosophers sitting around a table, each with a bowl of spaghetti. There are forks on the table between each pair of adjacent philosophers.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/at_the_table.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image courtesy of <a target="_blank" href="http://adit.io/posts/2013-05-11-The-Dining-Philosophers-Problem-With-Ron-Swanson.html">adit.io</a>.</em></p>
<p>Each philosopher can only do one thing at a time: think and eat. However, a philosopher can only eat spaghetti when they have both the left and right forks. A fork can only be held by one philosopher at a time.</p>
<p>After a philosopher finishes eating, they need to put down both the left and right forks so they're available to the others. A philosopher can take a fork as soon as it's available, but can only start eating once they have both forks.</p>
<p>The philosophers are famous for their appetites – they can all eat endlessly and never get full. On top of that, the bowls of spaghetti magically replenish no matter how much is eaten.</p>
<p>The problem is, how can can you ensure that no philosopher will starve, and that they can continue eating and thinking forever?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/mae-mu-Pvclb-iHHYY-unsplash.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-synchronization-and-deadlock">Synchronization and Deadlock</h3>
<p>In simple terms, the dining philosophers problem is an illustration of how synchronized access to a shared resource can result in creation of a deadlock situation.</p>
<p><strong>Synchronization</strong> is used to control concurrent access to a shared resource. This is necessary in any situation where multiple independent actors may be competing for the use of one resource like the forks. Since there is only one resource available, we use synchronization to prevent confusion and chaos.</p>
<p>A <strong>Deadlock</strong> is a system state where no progress is possible. This situation can occur when synchronization is enforced, and many processes end up waiting for a shared resource which is being held by some other process. Like with the philosophers who are either stuck eating or thinking, the processes just keep waiting and execute no further.</p>
<h3 id="heading-solutions">Solutions</h3>
<p>At first glance it appears like it would not be possible for a deadlock where all philosophers are stuck either eating or thinking. For example, pattern for each philosopher to follow might be:</p>
<blockquote>
<p>1: think until the left fork is available; when it is, pick it up;  </p>
<p>2: think until the right fork is available; when it is, pick it up;  </p>
<p>3: when both forks are held, eat for a fixed amount of time;  </p>
<p>4: then, put the right fork down;  </p>
<p>5: then, put the left fork down;  </p>
<p>6: repeat from the beginning.  </p>
<p>Source: <a target="_blank" href="https://en.wikipedia.org/wiki/Dining_philosophers_problem">Wikipedia</a></p>
</blockquote>
<p>There are many solutions possible to prevent deadlock. If we look closely, one problem in the algorithm above is that all philosophers have equal chance (have the same priority) of acquiring one fork. This prevents anyone from acquiring two forks and the whole system grinds to a halt.</p>
<p>Here are some possible solutions:</p>
<ol>
<li><strong>Priority</strong>: Some philosophers are assigned higher priority, so that the chance of acquiring both forks is increased.</li>
<li><strong>Preemption</strong> (Politeness): Philosophers relinquish the acquired fork without eating, in case the other fork is not available.</li>
<li><strong>Arbitration</strong>: A mediator allocates forks ensuring that two forks are given to one person, instead of one to many.</li>
</ol>
<p>Now that you know how to solve these logic puzzles, treat yourself to an endless bowl of spaghetti. You earned it.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
