<?xml version="1.0" encoding="UTF-8"?>
    <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
      <channel>
        <title>Irfan Ali</title>
        <link>https://irfanali.org/blog/</link>
        <description>Irfan Ali's Blog</description>
        <language>en-us</language>
        <atom:link href="https://irfanali.org/blog/rss.xml" rel="self" type="application/rss+xml" />
        <item>
      <title>A Simple Intuition for Open and Continuous Maps in Topology</title>
      <link>https://irfanali.org/blog/opencont</link>
      <guid>https://irfanali.org/blog/opencont</guid>
      <pubDate>Sun, 13 Sep 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h1>A Simple Intuition for Open and Continuous Maps in Topology</h1>
<p>I think it would be fair to say that many students in the starting lessons of general topology get a bit puzzled by the definition of continuous functions when they first encounter it:</p>
<p>Any function $f : X \rightarrow Y $ is continuous if the pre-image $f^{-1}(A)$ of every open set $A$ in $Y$ is open in $X$.</p>
<p>Doesn&#39;t it feel like the definition is backward?
Shouldn&#39;t a continuous function send open sets to open sets instead to preserve structure?
Well, functions which do that are called open functions (commonly open maps).</p>
<p>While we lean that continuity and openness are different conditions, some natural questions arise:</p>
<ol>
<li>What do they convey?</li>
<li>How are they different?</li>
</ol>
<p>Even though textbooks contain counterexamples to clarify that they do not imply each other, in many pedagogical settings the lesson ends with a stern warning to not get confused between the two without providing any intuition as to why they are so different.</p>
<p>This post is a mostly non-rigorous intuitive attempt to do that.</p>
<p>We start with some thoughts on the simple real line and then see some general reasons as to why continuity is not just openness.</p>
<h2>Continuity in Real Line</h2>
<p>Take the good-old $\epsilon-\delta$ definition of continuity:</p>
<p>A function $f : X \rightarrow Y$ is continuous at a point $c$ if for each $\epsilon \gt 0$ there exists a $\delta \gt 0$ such that $$ |x - c| &lt; \delta \implies |f(x) - f(c)| &lt; \epsilon $$</p>
<p>It first starts with a $\epsilon$ room around the image $f(c)$ and then says: no matter how small $\epsilon$ one chooses around image of $f(c)$ there must exist a $\delta$ room around $c$ such that every point in it must map inside the $\epsilon$ room.</p>
<p>We can see that this definition would prohibit any jumps.
Take the step function which takes the value $-1$ at all $x &lt; 0$ and $1$ at all $x \geq 0$.
We can find a room $(1 - \epsilon, 1 + \epsilon)$ around $f(0)$ and easily verify that it is impossible to find a $\delta$ room around $0$ which would map completely inside this target.</p>
<p>The discontinuity occurs because the step function <em>tears</em> the domain at $0$ to map one half of the domain to $-1$ and rest of the domain to $1$.
It is akin to <em>tearing</em> a strip of paper which represents the number line and mapping one part to $-1$ and another to $1$.
At the risk of losing some rigour, it might be helpful to rely on this analogy as a ladder to develop intuition.</p>
<p>Before we move forward, let&#39;s ponder one minor point.
It is common in intuitive explanations to hear that continuity means &quot;close enough points in domain must map to close enough points in codomain&quot;.
But we need to be a bit more careful about how we formalise this.</p>
<p>Imagine if we mistakenly represent this definition as follows:</p>
<p>A function $f : X \rightarrow Y$ is continuous at a point $c$ if for each $\epsilon$ there exists a $\delta$ such that $$ |x - c| &lt; \epsilon \implies |f(x) - f(c)| &lt; \delta $$</p>
<p>This is the same definition as before but we&#39;ve just switched the $\epsilon$ and $\delta$ in the condition.
Now we choose an $\epsilon$ and try to say that if $x$ stays within the $\epsilon$ room then the $f(x)$ must stay in the $\delta$ room.</p>
<p>But since a function does not have to map to every point in the $\delta$ room, this distorted definition does not do anything useful.
We can always satisfy this definition by taking a big enough $\delta$.</p>
<p>This shows that task of finding a $\delta$ to constrain $x$ so that $f(x)$ is constrained really does tell us something about the function.
But finding a $\delta$ to constrain $f(x)$ given that $x$ is constrained is not really a condition at all.
This happens because a function by definition must map every point in its domain somewhere but is not obligated to cover any point in the codomain.</p>
<p>Now, if we look closely enough the definition of open map is also very similar to our wrong $\epsilon-\delta$ but even more restrictive.
For a map to be open, every open set $A$ must map to some open set $B$ in codomain.</p>
<p>As mentioned before, this looks like a nice condition but note again what it proposes: image of every open set $A$ must be some open set $B$.
This means that every point in the open set $B$ must be mapped by some point in $A$.</p>
<p>This has a few unintended consequences which make open maps quite restrictive but at the same time does not ensure continuity in general.</p>
<h2>Open is Too Restrictive</h2>
<p>If a function $f$ must map every single $A$ to some open set $B$, it must ensure that whatever $B$ happens to, it is covered in the fullest.</p>
<p>This immediately disallows any functions which stay constant in any small interval in their domain.
If the function is constant in any interval $(a, b)$, then we can find smaller open interval $(a + \epsilon, b - \epsilon)$ ($\epsilon &lt; b - a$) which would map to a single point in the codomain, which obviously happens to be not open.</p>
<p>But constant functions are as benign a function as possible. It certainly is continuous, but it looks like it can not be open.
To borrow the analogy, this really means that for a function to be open, <em>squishing</em> to a point is not allowed, as the function must spread to whole of $B$ for every $A$ whatever open set $B$ happens to be.</p>
<p>Take another example, say, $f = x^2$. Though this is also a well-behaved continuous function on $\mathbb{R}$, it is not an open map.
If we take an open interval around $0$, say $(-1, 1)$, we can see that the image of this open interval is $[0, 1)$, which is clearly not open in $\mathbb{R}$.
This function is also continuous, but not open.</p>
<p>If we stretch the analogy further, we can see that $x^2$ <em>creases</em> the domain at $0$ in order to create a minima and revert back.</p>
<p>We can see that for a function to be open, both <em>squishing</em> and <em>creasing</em> of the domain are not allowed while mapping into the codomain.</p>
<p>This means that any nice continuous function which attains an extrema and changes direction in its domain ceases to be open.
It is a common exercise in analysis to prove that any continuous function $f : \mathbb{R} \rightarrow \mathbb{R}$ which happens to be open must be strictly monotonic.</p>
<p>If $f$ is continuous and open on interval $I$ but not strictly monotonic, then there would exist $$ u &lt; v &lt; w $$ in $I$ such that $f(v)$ is not between $f(u)$ and $f(w)$.
This means that $f$ would have a local extremum at some interior point in $I$.
And we have already seen that we can take a sufficiently small open interval around an extremum which maps to a half-open interval. This would mean $f$ is not open.</p>
<p>So, it is clear that open map is not a useful way to ensure continuity in functions as it disallows many nicely behaved continuous functions.</p>
<p>But let&#39;s look at this question from another angle: does the restriction of openness rule out any discontinuities.</p>
<p>It turns out the answer is a yes, but mostly accidently, I think.</p>
<p>It is easy to see that any open map would avoid any simple isolated discontinuities like a step function.
Since the step function we supplied above is constant, it is obviously not open.
But if we look at a strictly monotonic map with a discontinuity, say</p>
<p>$$ f(x) = \begin{cases} x &amp; \text{if } x &lt; 0 \\ x + 1 &amp; \text{if } x \geq 0 \end{cases} $$</p>
<p>We can see that it also fails to be open. We can see that for the interval $(-1, 1)$, the image is $(-1, 0) \cup [1, 2)$ which is clearly not open.</p>
<p>If we push this line further, we can convince ourselves that $f$ can not possibly have isolated discontinuities by which we mean that $f$ can not be discontinuous at any $c$ while still being continuous on some $(c-\epsilon, c)$ and $(c, c+\epsilon)$.</p>
<p>To see why that is true, we can see that $f(c)$ would lie either inside or outside the open images of these two intervals.</p>
<p>It can not lie outside as an isolated point, because then it would sit by itself in the image of $(c-\epsilon, c+\epsilon)$, making that image non-open as we have seen before.</p>
<p>But if it lies inside the image of either side intervals, say the left interval $(c - \epsilon, c)$, we can just find a smaller interval around $c$ to exclude that one point in $(c - \epsilon, c)$ which happened to also map to $f(c)$.</p>
<p>Strict monotonicity ensures that $f$ attains value $f(c)$ only once inside $(c - \epsilon, c)$.
But with the smaller interval, $f(c)$ would be back to being an isolated point outside both (shrunk) images and we already ruled that out.</p>
<p>So $f(c)$ can only sit right at the shared edge between the open images which just means $f$ is continuous at $c$.</p>
<h2>Open is Possibly Discontinuous</h2>
<p>It looks like disallowing a good chunk of functions also disallows simple types of discontinuity.
But, openness does not disallow all types of continuities.</p>
<p>We already saw that isolated discontinuities are disallowed if surrounded by continuity.</p>
<p>But the function can still diverge while being open.
$\tan(x)$ is a simple example. It diverges at every $x = (2n + 1) * \pi / 2$ but still happens to be open as any open interval in the domain yields either an open interval or a union of two infinite open intervals in the domain.</p>
<p>So, we have seen that open maps are good at ensuring that the function does not <em>squeeze</em> or <em>crease</em> the domain in ways that introduce <em>boundaries</em> in the images.
But open maps are not very helpful in ensuring that the function does not tear up the domain.
While simple tearings like isolated jumps are disallowed because they inevitably introduce <em>creases</em>, there are ways to construct open maps which defy continuity in pathological ways.</p>
<p>Since open maps only ensure that open sets map to open sets, if we can somehow manage to map every single open set to the entire real number line, the function does become open but would not be continuous.
A famous example is the Conway Base-13 function. It is based on an idea which roughly works like this:</p>
<ol>
<li>In Conway&#39;s construction, the number $x$ is written in Base-13 with the usual Base-10 digits and other symbols like $+$, $-$, and $.$</li>
<li>Now every number $x$ can be mapped to either $0$ or some other real $r$ based on some rule.</li>
<li>The rule looks like this: If the number when written in Base-13 had a <em>tail</em> which <em>looks like</em> a valid Base-10 real, then it is mapped to that real. If not, then it is mapped to $0$.</li>
<li>Take some number $x$ which when translated to Base-13 looks like: $$ +-492+1-2+3.14159... $$ then this number would get mappped to $\pi$.</li>
<li>But if the Base-13 expansion never had a tail which looked like a valid real say: $$ +-492+++... $$ it is mapped to $0$.</li>
</ol>
<p>Now, somewhat strangely, this function does happen to be an open map.
If we take any interval $(a, b)$, no matter how small, we can show that its image under the above function is the entire real line $\mathbb{R}$ (which is open in $\mathbb{R}$).
Take any arbitrary real $r$, all we need to do is to prefix a valid Base-13 <em>head</em> to produce a number $c$ such that:</p>
<ol>
<li>$c \in (a, b)$</li>
<li>tail of $c$ when written in Base-13 is equal to $r$</li>
</ol>
<p>Since head is the most significant part of the number and can be arbitrarily long before the <em>tail</em> begins, we can always find a $c$ which satisfies these.
But even though this function is open, it is obviously not continuous.</p>
<p>So, even though openness disallows simple enough <em>tears</em> in the number line because they introduce <em>creases</em> in some image and destroy openness.
But if we <em>tear</em> every single point in every single open interval and <em>scatter</em> them like dust particles over the entire number line, then openness is not violated as the whole real number line does not have any boundaries.</p>
<h2>Step Out of Reality</h2>
<p>So, now we have some idea of what open maps must be like on the real line.
But what about a general topology where there are no metrics or intervals?
What does continuity and openness mean in a space with vague or no notion of distance or proximity?</p>
<p>Before we go there we must notice that open sets in a general topology do not represent any property unlike on the real line.
In an unspecified general context, open sets are just sets which behave in a certain manner vis-a-vis set-theoretic operations.
Any collection of sets can be open if they preserve under arbitrary unions and finite intersections.
This is how the topology is defined in the first place.</p>
<p>Now, the curious thing about pre-image of a function is that it plays very very well with set-theoretic operations.
For any function $f : X \rightarrow Y$ and any $A$ and $B$ in $Y$
$$
    f^{-1}(A \cup B) = f^{-1}(A) \cup f^{-1}(B) \\
    f^{-1}(A \cap B) = f^{-1}(A) \cap f^{-1}(B) \\
    f^{-1}(Y - A) = X - f^{-1}(A)
$$</p>
<p>Regardless of the function, pre-images faithfully preserve unions and intersections.
This is largely due of the fact that a function by definition must map any point in the domain to a unique point in the codomain.</p>
<p>Note from the above equations that the set of pre-images in $X$ of all open sets in $Y$ of $f : X \rightarrow Y$, already fulfills the condition of being a topology on $X$.
This means that even before we define continuity or openness, pre-images automatically induce a topology on $X$ just by the virtue of their properties.</p>
<p>Said differently, if we consider the sets of points which got transformed into open sets in the codomain $Y$, we already know that they would be a valid topology.
Continuity only requires that they must have come from the topology already endowed in $X$ itself.</p>
<p>This begins to shine some light as to what continuity might be trying to portray.
It shows that whatever structure $Y$ has in terms of open sets, if we try to <em>pull back</em> the structure along the function to $X$, we just need to ensure that a similar or finer structure is available in $X$.</p>
<p>But when we talk about images, things are not quite smooth.
We started by thinking that images might <em>propagate</em> or <em>push</em> the structure from $X$ to $Y$, but alas that is not at all true.
Images simply don&#39;t play well with set-theoretic constructions.
Unions still work, but intersections don&#39;t work well.
$f(A \cap B)$ is not necessarily equal to $f(A) \cap f(B)$ because functions can map multiple points in the domain to the same point in the codomain.
Also $f(X - A)$ is not equal to $Y - f(A)$ in general.</p>
<p>So, even if we could ensure that open sets propagate from $X$ to $Y$ via images of open maps, we would never be able to ensure that they induce a topology on $Y$ from the topology of $X$ itself.</p>
<p>This is the reason that imposing conditions on the image of a function does not afford us any special behaviour when it comes to ensuring continuity.</p>
<p>If we try to round back to the analogy of <em>tearing</em> which continuity is supposed to disallow, we see that pre-images are the right choice.
If we take a simple step function on $\mathbb{R}$ which jumps on some $c$ in domain to $f(c)$ in range, then the pre-image of any sufficiently small open interval $(f(c) - a, f(c) + a)$ would obviously be outside the usual topology of the domain $\mathbb{R}$ (as it is the half-closed ray $[c, \infty)$)
This just means that the induced topology from all the pre-images of the step function contains at least one element different from the usual topology on $\mathbb{R}$.
This makes the pulled-back set fundamentally alien to the original topology of $\mathbb{R}$.
This is exactly the <em>tear</em> in the codomain which creates a set-theoretic <em>boundary</em> in the pre-image that the original domain topology never possessed.
It is detected when the induced topology in the domain is found to be different and irreconcilable from the endowed topology.</p>
<h2>Two Mindsets</h2>
<p>When we first study topology, our natural impulse is to think of functions as transformations which push the structure of a space forward onto another.
That is why we might think requiring open sets to map to open sets feels like the only logical way to preserve structure.</p>
<p>But as we see, open images as a condition is neither necessary nor sufficient and not even justified in general.
It either disallows benign smooth behaviour like local extrema or constancy but at the same time allow pathological maps to tear and scatter open sets everywhere.</p>
<p>Continuity works precisely under a <em>pullback</em> mindset.
Instead of trying to propagate the structure of the domain onto the codomain, we ask: does the domain possess enough structural resolution to make sense of the open sets in the codomain? If the answer is yes, the function is continuous.
This is why if we have the finest topology in the domain or the coarsest topology in the codomain, all functions become continuous.</p>
<p>But this should not mean that open maps are useless in total.
If a function which is both continuous and bijective (one-one onto) also happens to be open then its inverse becomes continuous which allows us to mark two topological spaces to be structurally identical (homeomorphism).</p>
<p>But taken separately, open and continuous maps measure two entirely different features of a transformation:</p>
<p>Continuity prevents <em>tearing</em> by ensuring the structure of codomain can be pulled back into the topology of the domain.
Openness prevents <em>squishing</em> and <em>creasing</em> by ensuring the room of the domain is distributed adequately across the image.</p>
]]></description>
    </item><item>
      <title>Poor Man's Time Machine</title>
      <link>https://irfanali.org/blog/repmin</link>
      <guid>https://irfanali.org/blog/repmin</guid>
      <pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h1>Poor Man&#39;s Time Machine</h1>
<p>Here is a simpler version of the famous repmin puzzle from Richard Bird&#39;s arsenal of functional tricks: Given a
non-empty array of numbers <code>a</code>, you have to find the smallest number <code>m</code> <em>and</em> replace every element of <code>a</code> with <code>m</code>
in a <em>single traversal</em>.</p>
<h2>Naive Non-Solution</h2>
<p>It is straightforward to build a naive non-solution which calculates the minimum value of <code>a</code> in one pass and then
mutates <code>a</code> in another pass:</p>
<pre><code class="language-javascript">function findMin(a) {
  let m = a[0]
  for (let i = 0; i &lt; a.length; i++) {
    if (a[i] &lt; m) {
      m = a[i]
    }
  }
  return m
}

function replaceMin(a, m) {
  for (let i = 0; i &lt; a.length; i++) {
    a[i] = m
  }
}

m = findMin(a)    // Pass #1
replaceMin(a, m)  // Pass #2
</code></pre>
<p>The question is: how do we perform these <em>temporally dependent</em> tasks in a single pass? That is, how do we go through
<code>a</code> to replace all its elements with <code>m</code> but somehow also calculate <code>m</code> at the same time?</p>
<p>Basically, we need to traverse <code>a</code> to calculate <code>m</code> and simultaneously replace it with some value <code>x</code> which we can
guarantee is going to be the minimum value?</p>
<p>But wait, won&#39;t that require sending a message to the present from the future?</p>
<p>So, is it necessary to invent a time-machine to solve this puzzle? Or could we do with something more modest: say, a
simple <strong>function</strong> and a tiny <strong>leap of faith</strong>.</p>
<h2>Unevaluated Value is Function</h2>
<p>To see how a simple function can help us, please note that the <strong>seemingly temporal</strong> dependency arises only because
<code>replaceMin(a, findMin (a))</code> forces the <em>traversal</em> in <code>findMin</code> before <em>replacement</em> in <code>replaceMin</code>.</p>
<p>If we could just find a way to represent the minimum value not as a <code>Number</code> in JavaScript but a <code>function</code> instead, the
temporal dependency would vanish because a function <em>doesn&#39;t need to be evaluated</em> immediately. We can just replace
every element of <code>a</code> with a function which would <em>evaluate</em> to the minimum value <em>later in the future</em>. This function is
then a proxy for <code>m</code> which delays the need to actually calculate <code>m</code> when we enter <code>replaceMin</code>.</p>
<p>Let&#39;s do this with a function <code>findAndReplaceMin</code> which calculates <code>m</code> but also replaces the elements of <code>a</code> by some
as-of-yet unrelated function <code>xf</code> in a single pass:</p>
<pre><code class="language-javascript">function findAndReplaceMin(a, xf) {
  let m = a[0]
  for (let i = 0; i &lt; a.length; i++) {
    if (a[i] &lt; m) {
      m = a[i]
    }
    a[i] = xf
  }
  return m
}
</code></pre>
<p>Note that <code>xf</code> is a function which is passed to <code>findAndReplaceMin</code> which replaces every element of the list <code>a</code> by
<code>xf</code>. We still need to choose <code>xf</code> so that every occurrence of <code>xf</code> eventually yields the minimum value, even though
that minimum has not yet been computed.</p>
<p>This seems like a problem. We&#39;re still stuck in a situation where evaluation of <code>m</code> requires <code>xf</code> be passed to
<code>findAndReplaceMin</code> but <code>findAndReplaceMin</code> itself needs <code>xf</code> to be passed before <code>m</code> has evaluated. </p>
<h2>Make Evidence for Faith</h2>
<p>This seems like real problem until we realise that we can <em>bootstrap</em> the evidence for our <em>faith</em> by saying this:</p>
<pre><code class="language-javascript">let m = findAndReplaceMin(a, () =&gt; m)
</code></pre>
<p>This seems paradoxical: how can <code>m</code> be used in its own definition?</p>
<p>The answer is subtle: we are NOT <em>using</em> <code>m</code> to calculate <code>m</code> but only <em>denoting</em> <code>m</code> in a function which calculates <code>m</code>
independently of the denotation.</p>
<p>More precisely, the closure <code>() =&gt; m</code> captures the undefined variable <code>m</code> without forcing the evaluation which would
happen only when we say <code>xf()</code>. And if you look carefully, we take care not to do that anywhere inside
<code>findAndReplaceMin</code> (more on this in a bit).</p>
<p>When the line <code>let m = findAndReplaceMin(a, () =&gt; m)</code> is executed in code, it would do <em>two separate tasks</em> in <em>one
single pass:</em></p>
<ol>
<li><em>Calculate</em> the minimum value by traversing <code>a</code> and <em>bind</em> it to <code>m</code></li>
<li><em>Replace</em> every element of <code>a</code> with the function <code>() =&gt; m</code> (without evaluating the function)</li>
</ol>
<p>Contrast the line above with the obviously invalid declaration: <code>let m = m</code> which immediately attempts to read the value
of <code>m</code> before it has been initialized.</p>
<p>By comparison, <code>let m = findAndReplaceMin(a, () =&gt; m)</code> never reads <code>m</code> during the construction of the closure. The
expression <code>() =&gt; m</code> merely records how to obtain <code>m</code> in the future. The actual lookup is postponed until the function
is called.</p>
<p>Note that after <code>findAndReplaceMin</code> has run, <code>a</code> is populated by <code>function</code>s and not <code>Number</code>s. So, when any element of
<code>a</code> is needed, we get it by calling <code>a[i]()</code> which would try to access the value bound to <code>m</code>. But note that that
doesn&#39;t need to run <code>findAndReplaceMin</code> again because <code>m</code> is already bound to the minimum value from Step 1.</p>
<p>In functional parlance, writing this self-referential declaration is close to what is called <strong>tying the knot</strong>. It
refers to the circular reference to <code>m</code>, which we fill from <code>findAndReplaceMin</code> in the future, but which we can still
use inside <code>findAndReplaceMin</code> in the present if we care not to inspect or evaluate it.</p>
<p>The key to this illusion of time-travel is the assignment <code>a[i] = xf</code> without inspecting <code>xf</code>. We can not afford to poke
<code>xf</code> (say by doing <code>xf()</code>) inside <code>findAndReplaceMin</code> because that would just force the evaluation of <code>m</code> ... inside the
evaluation of <code>m</code> ... I think you get where that would lead.</p>
<p>I should reiterate that no law of physics is being broken here and no information is actually travelling backwards in
time. We instinctively think of computation as a sequence of steps: first compute the minimum, then replace the
elements. But tying the knot invites us to think equationally instead. Rather than saying &quot;let me calculate <code>m</code> first&quot;,
we say &quot;here is how i&#39;d like <code>m</code> to be defined given <code>findAndReplaceMin</code>&quot; but with the added benefit that the definition
itself can be mutually recursive if we build it carefully.</p>
<p>We&#39;ve almost solved this puzzle, but you might now complain: we were asked to create an array of <code>Number</code>s but ended up
creating an array of <code>function</code>s instead.</p>
<p>And the answer is that with standard primitives, we can&#39;t solve this perfectly in JavaScript without leaving behind an
array of functions. (We could maybe pull off a solution using some advanced object metaprogramming, but that&#39;s probably
a topic for another blog post!)</p>
<h2>The Real Lazy Evaluation</h2>
<p>To check how we can do exactly what we sought out to, we look at a language where <strong>delayed evaluation</strong> is built-in:
<strong>Haskell</strong></p>
<p>Let&#39;s see how <code>findAndReplaceMin</code> would look like in Haskell:</p>
<pre><code class="language-haskell">findAndReplaceMin :: [Int] -&gt; Int -&gt; (Int, [Int])
findAndReplaceMin [y]     x  = (y, [x])
findAndReplaceMin (h : t) x  = (min g h, x : t&#39;)
  where (g, t&#39;) = findAndReplaceMin t x

let (m, b) = findAndReplaceMin a m
</code></pre>
<p>It almost mirrors the JavaScript logic, but recursively and immutably.</p>
<p>After <code>findAndReplaceMin</code> has run, the list contains <code>Int</code>s in Haskell. But the same is not possible in JavaScript. Why?</p>
<p>The main reason is that in Haskell, we do not need to rely on closures to delay evaluation because values are lazy by
default.</p>
<p>What that means in practice is that Haskell does not evaluate expressions just because they are passed as arguments or
stored inside data structures. Instead, expressions are represented as unevaluated values (called &quot;thunks&quot;) and
evaluated only when their values are demanded in some calculation. This evaluation strategy is known as <strong>call-by-need
evaluation</strong>. An expression like <code>findAndReplaceMin t x</code> can be easily written without worrying about <code>x</code> being
evaluated before <code>findAndReplaceMin</code> has finished. So, we don&#39;t need to resort to tricks like <code>() =&gt; m</code> just to delay
evaluation of <code>m</code>. The expression bound to <code>m</code> remains unevaluated until some operation (mostly pattern-matching, or
some function which needs <code>m</code>, like <code>+</code>, or <code>*</code>) actually demands its value.</p>
<p>Compare this to JavaScript, where if write <code>findAndReplace(a, m)</code>, both <code>a</code> and <code>m</code> would be evaluated <strong>before</strong> being
passed to the function, which is why we need to &quot;wrap&quot; <code>m</code> inside <code>() =&gt; ...</code> to prevent JavaScript from poking. This
style of evaluation is called <strong>call-by-value evaluation</strong> and is common in languages like C, Python, and JavaScript.</p>
<p>Also, tying the knot is relatively easier in Haskell. In Haskell, we can just write <code>m</code> on both sides of <code>let (m, b) = findAndReplaceMin a m</code>. A similar attempt in JavaScript is bound to fail, which is also why we took care to hide <code>m</code>
inside <code>() =&gt; m</code>. But Haskell can construct cyclic graphs of thunks. The self-reference in <code>let (m, b) = findAndReplaceMin a m</code> does not require the runtime to know the value of <code>m</code> immediately. Instead, it creates a network
of dependencies in which <code>m</code> and <code>b</code> can mutually refer to each other. Laziness ensures that these references are only
followed when their values are actually needed.</p>
<h2>Don&#39;t Peek Too Soon!</h2>
<p>So it turns out we didn&#39;t need a time machine after all: just a function, a carefully deferred lookup, and the
discipline not to peek too soon. But more importantly, in both languages, the trick explicitly relies on the same <strong>leap
of faith</strong>: while the lazy evaluation (Haskell) and wrapper lambda (JavaScript) prevent automatic evaluation by runtime,
we ourselves can not afford to peek into them (by pattern-matching <code>m</code> in Haskell or calling <code>xf</code> in JavaScript) before
<code>findMinAndReplace</code> finishes execution.</p>
<p>If we get too impatient and lose faith, exactly the same fate awaits us in both Haskell and JavaScript: an <em>infinite
abyss of doubt</em> till time ends!</p>
]]></description>
    </item><item>
      <title>A Gentler Guide to Y and Z Combinators</title>
      <link>https://irfanali.org/blog/zcom</link>
      <guid>https://irfanali.org/blog/zcom</guid>
      <pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h1>A Gentler Introduction to the Y and Z Combinators</h1>
<p>Let’s try to solve a simple enough puzzle in JavaScript: You have to write a function <code>fact(n)</code> which calculates the
factorial of a number <code>n</code>, but you can not use loops (<code>for</code>, <code>while</code>, etc), recursion, or even declarations (<code>let</code>,
<code>const</code>, etc).</p>
<h2>Non-Solution: Direct Recursion</h2>
<p>We start with a canonical recursive non-solution to know what we&#39;re aiming for, in terms of behaviour:</p>
<pre><code class="language-javascript">function fact(n) {
  if (n === 0) {
    return 1
  }
  else {
    return n * fact(n - 1)
  }
}
</code></pre>
<p>This is a non-solution because we can not use recursion, which means we can not call the function <code>fact</code> from inside the
definition of the function <code>fact</code>.</p>
<h2>Non-Solution: Mutual Recursion</h2>
<p>So, running <code>for</code> loops or calling <code>fact</code> from itself is not allowed. Then, what should we do? Can we call <code>fact</code> from
some other function and try to arrive at the same behaviour?</p>
<p>After some thinking, we can write a function <code>factf</code> which calculates factorial but doesn&#39;t call itself:</p>
<pre><code class="language-javascript">function factf(n) {
  if (n === 0) {
    return 1
  }
  else {
    return n * factg(n - 1)
  }
}

function factg(n) {
  if (n === 0) {
    return 1
  }
  else {
    return n * factf(n - 1)
  }
}
</code></pre>
<p>This works. <code>factf(n)</code> does calculate the factorial. So, are we done? Well, we did avoid explicit call for <code>factf</code> but
it&#39;d be a lie to say that this doesn&#39;t use recursion.</p>
<p>This solution calls for a new rule: <strong>mutual recursion</strong> is also not allowed.</p>
<h2>Non-Solution: Recursion via Indirect Self-Reference</h2>
<p>But, can we still learn something from the previous attempt? We know that if we decide to avoid loops, we definitely
need to emulate a call to something like <code>fact</code> inside <code>fact</code>. What if we don&#39;t call <code>fact</code> inside <code>fact</code> directly but
define a generator function <code>factgen</code> and pass a function which gets called instead:</p>
<pre><code class="language-javascript">function factgen(self, n) {
  if (n === 0) {
    return 1
  }
  else {
    return n * self(self, n - 1)
  }
}

let fact = n =&gt; factgen(factgen, n)
</code></pre>
<p>To see how it works, take <code>fact(4)</code>. It is just <code>factgen(factgen, 4)</code> which evaluates to <code>4 * self(self, 3)</code> which is
just <code>4 * factgen(factgen, 3)</code>, which just reduces to <code>4 * 3 * factgen(factgen, 2)</code> and so on. Every <code>factgen(factgen, n)</code> call essentially becomes <code>n * factgen(factgen, n - 1)</code> until <code>n</code> becomes <code>0</code>.</p>
<p>It seems like we&#39;ve solved our puzzle:</p>
<ol>
<li><code>fact</code> did not call itself in its own definition (explicit recursion)</li>
<li><code>fact</code> also did not call any other function which called <code>fact</code> indirectly (mutual recursion).</li>
</ol>
<p>But alas, even though <code>fact</code> did not call itself directly or indirectly, <code>factgen</code> did receive a reference to itself and
used the reference in the definition of <code>fact</code>. And if we look closely at our challenge, we should not use a
declaration.</p>
<p>If we really vowed to <code>let</code> go of declarations, we should avoid using <code>function</code> too. This calls for another rule: all
declarations (including <code>function</code>) are not allowed.</p>
<p>We&#39;d use <strong>only anonymous functions</strong> like <code>(x, y) =&gt; { .. }</code> in our solution.</p>
<h2>Recursion is Self-Reference</h2>
<p>But like before, let&#39;s see if we can get some intuition from the definition above. If we zoom in on the recursive part,
we see that <code>self(self, n)</code> has a curious property. Evaluating it does not eliminate the self-reference. Instead, it
eventually produces another call involving the same pattern, namely <code>self(self, n - 1)</code> embedded inside a larger
expression. The argument changes, but the self-application survives. This is not even specific to the definition of
<code>fact</code> given here. If, say, there was a function <code>f</code> defined like this:</p>
<pre><code class="language-javascript">function f(g, n, s) {
  console.log(s)
  if (n &lt; 10) {
    g(g, n + 1, s)
  }
}
</code></pre>
<p>and then called like this: <code>f(f, 0, &quot;hello, world&quot;)</code>. We&#39;d get <code>&quot;hello, world&quot;</code> printed repeatedly until <code>n</code> reaches
<code>10</code> (if stack size allows). That&#39;s basically a while loop.</p>
<p>So, our venture teaches us that the pattern <code>g(g, ...)</code> captures the core mechanism needed to create recursion. It
<em>results in itself when it executes</em>, which allows it to use the same behaviour but with different inputs to emulate
iteration.</p>
<h2>Self-Replication without Self-Reference</h2>
<p>Let&#39;s see if we can extract this pattern into a function to gain more insight:</p>
<pre><code class="language-javascript">let rep = x =&gt; x(x)
</code></pre>
<p>(Don&#39;t worry about the <code>let</code>, this is not an attempt at solving the challenge and we&#39;d remove it later.)</p>
<p>Clearly, <code>rep</code> doesn&#39;t do anything in particular. It just takes a higher-order function <code>x</code> and calls <code>x</code> with argument
<code>x</code>. But something interesting happens when we call rep with itself as an argument. If we reason purely in terms of
expressions and forget evaluation for a moment, then <code>rep(rep)</code> rewrites to <code>rep(rep)</code> again.</p>
<p>(Operationally, however, a JavaScript engine attempting to evaluate this expression would recurse forever and eventually
overflow the stack. We&#39;d handle this problem later. For now, we are interested only in the equational behaviour of the
expression.)</p>
<p>In other words, if written without <code>rep</code>, the code:</p>
<pre><code class="language-javascript">(x =&gt; x(x))(x =&gt; x(x))
</code></pre>
<p>evaluates to itself. Please note that we just obtained self-application without loops, recursion, or declarations. We
just used an anonymous function and literally wrote the function twice by hand.</p>
<p>But what good is a self-replicating code like <code>rep</code> if we can&#39;t use this machinery to do some real task, like
calculating the factorial. To get something useful with <code>rep</code>, we&#39;d need to expand on this idea.</p>
<p>How about we do the obvious and stick a function, say <code>h</code> (assumed to be in scope) inside the definition of <code>rep</code>: <code>let rep = x =&gt; h(x(x))</code></p>
<p>Now, something even more interesting happens if we reduce <code>rep(rep)</code> (Again, we just mean reduction in equational sense,
not how it would run on the system.)</p>
<p><code>rep(rep)</code> just reduces to:</p>
<pre><code class="language-javascript">(x =&gt; h(x(x)))(x =&gt; h(x(x)))
</code></pre>
<p>which is nothing but:</p>
<pre><code class="language-javascript">h((x =&gt; h(x(x)))(x =&gt; h(x(x))))
</code></pre>
<p>which, if looked closely, is just <code>h(rep(rep))</code> expanded. In other words, <code>rep(rep)</code> evaluates to <code>h(rep(rep))</code> with our
new definition of rep.</p>
<p>This is more interesting. Now we not only have replication, but we can run some function on the result. This is very
close to the recursion we&#39;re looking for.</p>
<h2>Enter Y</h2>
<p>What if we wrap this whole self-replicating machine inside a function <code>Y</code> which accepts a payload <code>h</code>:</p>
<pre><code class="language-javascript">let Y = (h) =&gt; {
  let rep = x =&gt; h(x(x))
  return rep(rep)
}
</code></pre>
<p>The <code>Y</code> is basically a higher-order function which takes a function <code>h</code> and returns the self-replicating code we wrote
before, parametrised to function <code>h</code>.</p>
<p>But there are 2 problems now:</p>
<ol>
<li>It is not clear how to use <code>Y</code> to emulate recursive behaviour.</li>
<li>As many of you might be complaining now, the way everything above is written, from <code>rep</code> to <code>Y</code>, it won&#39;t even
  work because it would never stop evaluating.</li>
</ol>
<p>Let&#39;s tackle the first problem first as it looks easier. Assume that the second problem is solved, ie, assume we have
some way through which we can delay the call to <code>rep(rep)</code> till just the right moment. Now, given delayable <code>Y</code>, how
then would we come up with the factorial function?</p>
<p>To see what <code>Y</code> does, let&#39;s see what <code>Y(f)</code> means. If evaluation can be delayed at will, we can see that <code>Y(f)</code> would
reduce to <code>rep(rep)</code> which would just evaluate to <code>f(rep(rep))</code> which is identical to <code>f(Y(f))</code> in terms of behaviour.</p>
<p>So <code>Y(f)</code> reduces to <code>f(Y(f))</code>, which reduces to <code>f(f(Y(f)))</code>, and so on. Assuming it exists, let&#39;s call the result of
<code>Y(f)</code> to be <code>p</code>. What we&#39;ve shown is that <code>p</code> satisfies:</p>
<pre><code class="language-javascript">p = f(p)
</code></pre>
<p>Feeding <code>p</code> back through <code>f</code> gives <code>p</code> again.</p>
<p>An object with that property is called a fixed point of <code>f</code>. So <code>Y(f)</code> is just the fixed point of <code>f</code>.</p>
<p>Now admittedly, this is just a tidy definition and not yet a working program. Let me get there via a short detour, on
the promise that we&#39;re close.</p>
<h2>Fixed-Points and Recursion</h2>
<p>Let&#39;s go back to our <code>factgen</code> from earlier and try to write a curried version which takes only a single argument and
returns a function:</p>
<pre><code class="language-javascript">function factgen(factaux) {
  return n =&gt; {
    if (n === 0) {
      return 1
    }
    else {
      return n * factaux(n - 1)
    }
  }
}
</code></pre>
<p>This <code>factgen</code> is the curried analogue to the earlier <code>factgen</code>. It takes a function <code>factaux</code> and returns a function which
takes a number <code>n</code> and returns its factorial by calling <code>factaux</code> internally.</p>
<p>If hypothetically the declaration <code>let fact = factgen(fact)</code> were valid JavaScript, it would define the factorial. Alas,
JavaScript refuses to accept this because it evaluates the right-hand side first and it cannot pass <code>fact</code> into
<code>factgen</code> before <code>fact</code> has finished being initialised (JavaScript is inadvertently applying the rules of the challenge
for us here.)</p>
<p>But like before, every failed attempt is an opportunity to look for a new idea. In this case, if we observe closely, we
can see that the equality <code>let fact = factgen(fact)</code> describes something familiar about <code>fact</code>. Going back to our
discussion of <code>Y</code>, if we think that <code>fact</code> is <code>p</code> and <code>factgen</code> is <code>f</code>, then a curious fact reveals itself: <code>fact</code> is
nothing but the fixed point of the function <code>factgen</code>!</p>
<p>Note that what was said above is not dependent on the details of the function factorial, which sit cozily inside
<code>factgen</code>, untouched by our discussions on fixed points. In fact, the following observation is true for all recursive
functions:</p>
<p>Every recursive function (like <code>fact</code>) can be expressed as a fixed point of an appropriately defined function (like
<code>factgen</code>).</p>
<p>To think about why it is true, ponder over the fact that any function satisfying the above equation would behave exactly
like the recursive function <code>fact</code> we needed to define in the first place as <code>factgen(fact)</code> just produces a function
whose recursive calls have been delegated to <code>fact</code>, which is what we wanted initially.</p>
<p>While JavaScript rejects this definition, what if we had a way to calculate the fixed point of <code>factgen</code> which doesn&#39;t
involve a call to as-of-yet undefined <code>fact</code>?</p>
<h2>Re-enter Y</h2>
<p>Indeed we do. <code>Y(factgen)</code> is nothing but the fixed point of <code>factgen</code>. So we could now just define <code>fact</code> like:</p>
<pre><code class="language-javascript">function factgen(factaux) {
  return n =&gt; {
    if (n === 0) {
      return 1
    }
    else {
      return n * factaux(n - 1)
    }
  }
}

let fact = Y(factgen)
</code></pre>
<p>We&#39;ve solved our main problem: <code>fact</code> is no longer defined in terms of itself but as the fixed point of <code>factgen</code>.
(Note that we&#39;re still using <code>let</code> and <code>function</code> which we&#39;d get rid of in a while.)</p>
<p>Now that we have a solution using <code>Y</code>, let&#39;s come to the second problem. So we assumed that we could delay the repeated
evaluation of <code>Y</code> till it&#39;s needed. But how can we do that?</p>
<p>Note that in JavaScript, every function needs to evaluate its arguments before it can be called. In technical terms,
this is known as eager evaluation. When we try to evaluate <code>Y(factgen)</code>, it reduces to <code>factgen(Y(factgen))</code>. But this
never hands control to <code>factgen</code> because the argument <code>Y(factgen)</code> to <code>factgen</code> must be fully evaluated first which
triggers the exact same loop, looping forever before a single line of factgen actually executes.</p>
<h2>The Lazy Cousin</h2>
<p>What if there was a simple way to introduce delay in the evaluation of a value like <code>Y(factgen)</code>? How about trying to
wrap it inside another function instead?</p>
<p>We tweak <code>Y</code> to wrap the argument of the call <code>h(x(x))</code> and replace it with <code>h(v =&gt; x(x)(v))</code>:</p>
<pre><code class="language-javascript">let Z = (h) =&gt; {
  let rep = x =&gt; h(v =&gt; x(x)(v))
  return rep(rep)
}
</code></pre>
<p>If we look closely, <code>Z</code> is almost identical to <code>Y</code>. The only difference is that the self-reference is wrapped inside
another function: <code>v =&gt; x(x)(v)</code> This wrapping postpones the self-application until it is actually needed.</p>
<p>But conceptually, the behaviour remains the same.</p>
<p><code>Z(f)</code> evaluates to <code>rep(rep)</code>, which expands to <code>h(v =&gt; rep(rep)(v))</code>.</p>
<p>Now, the function <code>v =&gt; rep(rep)(v)</code> is not literally the same object as <code>rep(rep)</code>. The difference is that it delays
the self-application until an argument is supplied. But for our purposes, <code>v =&gt; rep(rep)(v)</code> plays the same role as
<code>rep(rep)</code>. Whenever it is eventually called, it re-enters the same self-referential computation that <code>rep(rep)</code> would
have triggered immediately. The difference is merely one of timing.</p>
<p>Now, let&#39;s get back to factorial. Amazingly, <code>Z</code> is so much like <code>Y</code> that we can just replace <code>Y</code> by <code>Z</code> in our
definition and it works.</p>
<p><code>Z(factgen)</code> evaluates to <code>factgen(v =&gt; rep(rep)(v))</code> which is just the function:</p>
<pre><code class="language-javascript">n =&gt; {
  if (n === 0) {
    return 1
  }
  else {
    return n * (v =&gt; rep(rep)(v))(n - 1)
  }
}
</code></pre>
<p>Here <code>(v =&gt; rep(rep)(v))(n - 1)</code> is just <code>rep(rep)(n - 1)</code>, and <code>rep(rep)</code> expands back to <code>factgen(v =&gt; rep(rep)(v))</code>.
The self-reference is recreated but computation is delayed because lambda <code>v =&gt; rep(rep)(v)</code> doesn&#39;t need to be reduced
further until the whole expression is applied to produce some result.</p>
<p>So calling this on <code>4</code> gives <code>4 * rep(rep)(3)</code>, which gives <code>4 * 3 * rep(rep)(2)</code>, and so on down to <code>n === 0</code>, where
the recursion bottoms out at <code>1</code> and the else branch is finally skipped</p>
<p>Here is a visual trace of the evaluation:</p>
<pre><code>Z(factgen)(4)

factgen(v =&gt; rep(rep)(v))(4)

4 * (v =&gt; rep(rep)(v))(3)

4 * rep(rep)(3)

4 * factgen(v =&gt; rep(rep)(v))(3)

4 * 3 * (v =&gt; rep(rep)(v))(2)

4 * 3 * rep(rep)(2)

4 * 3 * 2 * (v =&gt; rep(rep)(v))(1)

4 * 3 * 2 * rep(rep)(1)

4 * 3 * 2 * factgen(v =&gt; rep(rep)(v))(1)

4 * 3 * 2 * 1 * (v =&gt; rep(rep)(v))(0)

4 * 3 * 2 * 1 * rep(rep)(0)

4 * 3 * 2 * 1 * factgen(v =&gt; rep(rep)(v))(0)

4 * 3 * 2 * 1 * 1
</code></pre>
<p>Notice that in the last step, it doesn&#39;t matter what the argument to <code>factgen</code> is. No recursion is triggered and the
function returned terminates if <code>n</code> is <code>0</code>.</p>
<p>So, to conclude, the expression <code>Z(factgen)</code> behaves exactly like a function which calculates a factorial. You can test
it by calling <code>console.log(Z(factgen)(4))</code>.</p>
<p>In fancy parlance, the function <code>Y</code> is called the <strong>Y combinator</strong>. It is a fixed-point combinator: given a function, it
produces one of its fixed points.</p>
<p>But the version that actually works in JavaScript is usually called the <strong>Z combinator</strong>. Conceptually it performs the
same task as Y, but introduces an extra layer of indirection so that the construction can survive the eager evaluation
strategy.</p>
<h2>The End</h2>
<p>So, after many an ordeal and digression, we have finally defined a factorial without iteration or recursion. And if you
insist, even without declarations:</p>
<pre><code class="language-javascript">(f =&gt; (x =&gt; f(v =&gt; x(x)(v))) (x =&gt; f(v =&gt; x(x)(v)))) (cb =&gt; n =&gt; (n === 0) ? 1 : n * cb(n - 1))
</code></pre>
<p>Case closed.</p>
]]></description>
    </item>
      </channel>
    </rss>