<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Ankit Gadiya - programming-theory</title>
    <subtitle>My little corner of the Internet 🙌</subtitle>
    <link rel="self" type="application/atom+xml" href="https://ankit.earth/tags/programming-theory/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://ankit.earth"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-11-24T00:00:00+00:00</updated>
    <id>https://ankit.earth/tags/programming-theory/atom.xml</id>
    <entry xml:lang="en">
        <title>Church Booleans</title>
        <published>2025-11-24T00:00:00+00:00</published>
        <updated>2025-11-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Ankit Gadiya
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ankit.earth/blog/church-booleans/"/>
        <id>https://ankit.earth/blog/church-booleans/</id>
        
        <content type="html" xml:base="https://ankit.earth/blog/church-booleans/">&lt;p&gt;In the last post, I covered the basics of &lt;a href=&quot;&#x2F;blog&#x2F;lambda-calculus&#x2F;&quot;&gt;Lambda
calculus&lt;&#x2F;a&gt; and its lack of built-in data types. Church
encoding defines a way to represent data types as pure functions. In this post,
I&#x27;ll explore church booleans and control flow, and implement them using Python.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;booleans-as-functions&quot;&gt;Booleans as functions&lt;&#x2F;h2&gt;
&lt;p&gt;Booleans are plain functions that take two arguments.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;true&lt;&#x2F;code&gt; always returns the first argument,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;false&lt;&#x2F;code&gt; always returns the second argument.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code&gt;𝛌a.𝛌b a   # True
𝛌a.𝛌b b   # False
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In Python, I can define these two functions for booleans.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def true(a, b):
    return a

def false(a, b):
    return b

true(1, 2)  # -&amp;gt; 1
false(1, 2) # -&amp;gt; 2
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To understand why this works more clearly we&#x27;ll look at control flow.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;control-flow-constructs-in-python&quot;&gt;Control flow constructs in Python&lt;&#x2F;h2&gt;
&lt;p&gt;In Python, &lt;code&gt;if&lt;&#x2F;code&gt; is a special language construct that looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;if TRUE:
  BRANCH 1
else:
  BRANCH 2
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Why can&#x27;t it be a function? I&#x27;ll try to define &lt;code&gt;if&lt;&#x2F;code&gt; as a function using the
boolean functions.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def bad_if(bool_func, b1, b2):
    return bool_func(b1, b2)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If the implementation is correct, it must evaluate exactly one branch. To check
this, I&#x27;ll call the &lt;code&gt;bad_if&lt;&#x2F;code&gt; function with two branches.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python-console&quot;&gt;&amp;gt;&amp;gt;&amp;gt; bad_if(false, 2+2, 2-2)
0
&amp;gt;&amp;gt;&amp;gt; bad_if(true, 2+2, 2-2)
4
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At first glance it looks correct. But we need to make sure that Python isn&#x27;t
evaluating both branches. I&#x27;ll call &lt;code&gt;bad_if&lt;&#x2F;code&gt; with two &lt;code&gt;print&lt;&#x2F;code&gt; statements
instead.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; bad_if(true, print(&amp;quot;Branch 1&amp;quot;), print(&amp;quot;Branch 2&amp;quot;))
Branch 1
Branch 2
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This shows that Python did actually evaluate both branches. This happens because
Python eagerly evaluates the arguments before the function call. It then passes
the value of the evaluation to the function. So the &lt;code&gt;bad_if&lt;&#x2F;code&gt; call is equivalent
to this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;b1 = print(&amp;quot;Branch 1&amp;quot;)   # -&amp;gt; None
b2 = print(&amp;quot;Branch 2&amp;quot;)   # -&amp;gt; None
bad_if(true, b1, b2)     # -&amp;gt; None
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Because of the eager evaluation, Python needs a special syntax for control flow
to avoid accidentally evaluating both branches.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conditionals-in-lambda-calculus&quot;&gt;Conditionals in Lambda Calculus&lt;&#x2F;h2&gt;
&lt;p&gt;In contrast to Python&#x27;s eager evaluation, Lambda calculus only evaluates the
arguments when
&lt;a href=&quot;&#x2F;blog&#x2F;lambda-calculus&#x2F;#function-application-and-b-reduction&quot;&gt;β-reduction&lt;&#x2F;a&gt;
requires the value. So a function call similar to &lt;code&gt;bad_if&lt;&#x2F;code&gt; behaves very
differently in Lambda calculus.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;bad_if(true, print(&amp;quot;Branch 1&amp;quot;),
             print(&amp;quot;Branch 2&amp;quot;))
true(print(&amp;quot;Branch 1&amp;quot;), print(&amp;quot;Branch 2&amp;quot;))   # Reduce bad_if
print(&amp;quot;Branch 1&amp;quot;)                            # Reduce true, returns first argument.
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Because of lazy evaluation, if the argument is not used then it will not be
evaluated. So, we can define &lt;code&gt;if&lt;&#x2F;code&gt; as a pure function in Lambda calculus.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;𝛌f.𝛌a.𝛌b (f a b)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To force lazy evaluation in Python, we can wrap the branches in a &lt;strong&gt;Lambda&lt;&#x2F;strong&gt;. We
can then call the functions returned by the boolean function.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def good_if(bool_func, b1, b2):
    # Notice we are calling the result from bool_func.
    return bool_func(b1, b2)()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ll call the &lt;code&gt;good_if&lt;&#x2F;code&gt; with two branch lambdas. We can see that it correctly
evaluates exactly one branch.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python-console&quot;&gt;&amp;gt;&amp;gt;&amp;gt; good_if(true, lambda: print(&amp;quot;branch 1&amp;quot;), lambda: print(&amp;quot;branch 2&amp;quot;))
branch 1
&amp;gt;&amp;gt;&amp;gt; good_if(false, lambda: print(&amp;quot;branch 1&amp;quot;), lambda: print(&amp;quot;branch 2&amp;quot;))
branch 2
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Church encoding shows how expressive Lambda calculus can be, and how
higher-level constructs can be built from regular functions. There&#x27;s much more
to explore in future posts.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Lambda Calculus</title>
        <published>2025-11-17T00:00:00+00:00</published>
        <updated>2025-11-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Ankit Gadiya
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://ankit.earth/blog/lambda-calculus/"/>
        <id>https://ankit.earth/blog/lambda-calculus/</id>
        
        <content type="html" xml:base="https://ankit.earth/blog/lambda-calculus/">&lt;p&gt;Many modern programming languages trace their roots back to Lisp, which in turn
builds on ideas from lambda calculus. In this post, I explore the core ideas
of lambda calculus.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-lambda-calculus&quot;&gt;What is lambda calculus&lt;&#x2F;h2&gt;
&lt;p&gt;For the longest time, I had the impression that lambda calculus has something to
do with the calculus from high school mathematics. I was wrong, &lt;em&gt;calculus&lt;&#x2F;em&gt; here
simply means to calculate.&lt;&#x2F;p&gt;
&lt;p&gt;Lambda calculus is a system to define any computation. Think of it like an
abstract programming language to express programs. It does so using simple
ideas: variables, functions and their application.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;basic-constructs&quot;&gt;Basic constructs&lt;&#x2F;h2&gt;
&lt;p&gt;Lambda calculus defines two basic constructs. These are sufficient to model any
computation.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Variables&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Variables are symbols that act as identifiers in the computation. Variables
can be free or bound to a function&#x27;s scope (parameter).&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;x           # x is free
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But the variables don&#x27;t behave like programming languages. So, you cannot
assign values to them.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Functions&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Functions take arguments and perform some computation. The arguments are
&lt;em&gt;bound&lt;&#x2F;em&gt; variables in the function&#x27;s scope.&lt;&#x2F;p&gt;
&lt;p&gt;This is a simple function that takes one argument &lt;code&gt;x&lt;&#x2F;code&gt; and returns it.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;𝛌y. y       # y is bound
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Sidenote: A function can only take a single argument. But multiple arguments
can be simulated by defining nested functions.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;𝛌y.𝛌z y
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;function-application-and-b-reduction&quot;&gt;Function application and β-reduction&lt;&#x2F;h2&gt;
&lt;p&gt;Functions are not useful on their own. To use the functions, you can apply them
to arguments. Lambda calculus defines how functions are computed: β-reduction.&lt;&#x2F;p&gt;
&lt;p&gt;β-reduction is a fancy term for repeated substitution. The functions are
computed by progressively substituting bound variables within the function body.&lt;&#x2F;p&gt;
&lt;p&gt;This is a contrived example to visualize β-reduction properly. Here is a nested
function that takes two arguments and by repeated substitution, we get the
result.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;(𝛌x.𝛌y. x) A B

(𝛌y. A) B        # First reduction for outer function.

A                # Second reduction for inner function.
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;That&#x27;s it, there is no construct for conditionals, data types, loops etc. But
curiously, it is possible to encode high-level constructs using these basic
ideas. In a future post, I&#x27;ll explore &lt;em&gt;church encodings&lt;&#x2F;em&gt; to encode conditionals
and integers in lambda calculus.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
