<?xml version="1.0" encoding="utf-8"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
      <id>https://bhoot.dev/</id>
      <title>Jayesh Bhoot's Ghost Town – All Posts</title>
      <updated>2026-03-10T02:53:56.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <link href="https://bhoot.dev/feed.xml" rel="self"/>
      <entry>
      <id>urn:uuid:f11a08e7-1b45-457c-9a5f-c213095a1965</id>
      <title type="html">My understanding of SBI Max Gain loan account</title>
      <updated>2026-03-10T02:53:56.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is my rudimentary, superficial, not-yet-verified understanding of SBI Max Gain account. &lt;strong&gt;Please verify with a qualified person (like an SBI branch manager) before making a financial decision.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some banks in India offer a loan overdraft facility, where your loan account is also an overdraft account. You can park extra funds in it, which, while earning zero interest, would reduce the principal of the loan during interest calculation.&lt;/p&gt; 

&lt;p&gt;SBI Max Gain account is such a facility from State Bank of India. I have this account for my home loan.&lt;/p&gt;

&lt;p&gt;After getting stumped for a long time by a mismatch between my finance sheet and the available balance shown in my Max Gain account, I sat down to understand it. In the process, I noticed a nuance, which when discovered, seemed like a given.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The basic rule is this: For a Max Gain account, any reduction effected in the principal amount of the loan, apart from the one effected according to the amortised schedule, is temporary and withdrawable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt; Let&#39;s see how this plays out in different scenarios.&lt;/p&gt;

&lt;h2&gt; EMI payments &lt;/h2&gt;

&lt;p&gt;Keeping in mind the unpredictability of the floating interest rate, I have set up my monthly payments to be higher than the scheduled EMIs.&lt;/p&gt;

&lt;p&gt;In a normal loan, such a payment would be split into: pre-calculated interest of that month; pre-calculated principal amount for that month; and excess goes to principal payment. The third component reduces the total principal amount faster than the amortized schedule would.&lt;/p&gt;

&lt;p&gt;In SBI Max Gain, the treatment is similar, but with a slight twist: the excess goes to the overdraft account, where it reduces the principal amount of the loan for the time it sits there, and is also withdrawable.&lt;/p&gt;

&lt;h2&gt; Part payments or pre-payments &lt;/h2&gt;

&lt;p&gt;Another way to reduce principal amount is to make explicit part payments. There is a general tendency to pay back a loan as fast as affordable. Part payment is the main tool for it, where you repeatedly, as and when convenient, pay back the principal amount ahead of the amortised schedule.&lt;/p&gt;

&lt;p&gt;In a normal loan, while a part-payment permanently reduces the principal amount by that amount, you also part with that amount permanently.&lt;/p&gt;
&lt;p&gt;In Max Gain, a part-payment is just depositing whatever lumpsum you have in the overdraft account. The principal amount of the loan is reduced only for the time it sits there. There is no permanent reduction of the principal amount other than what the amortised schedule effects. But that amount also remains available to you.&lt;/p&gt;

&lt;p&gt;Now imagine that over the years, for a normal loan of 50 lakhs, the part payments have amounted to 30 lakhs. In Max Gain, those 30 lakhs basically sit in the overdraft account, still usable for other purposes.&lt;/p&gt;

&lt;p&gt;One might think it an idiocy to just let 30 lakhs stew in a zero-interest earning bank account. Why not invest them? First, disposable income would already be responsibly and proportionately split between investments and part payments, thus taking care of investments beforehand. Second, in case of the normal loan, the part payment amount is permanently gone. At least its still available in case of Max Gain.&lt;/p&gt; 

&lt;h2&gt; There is a catch &lt;/h2&gt;

&lt;p&gt;My bank branch told me that there is a cap on the amount that would reduce the principal of the loan. So if the principal is 50 lakhs, then that cap could be around 30 lakhs. Overdraft balance beyond 30 lakhs don&#39;t reduce the loan principal. Its best to inquire with the branch manager.&lt;/p&gt;

&lt;h2&gt; Foreclosing a loan &lt;/h2&gt;

&lt;p&gt;While I haven&#39;t yet verified it with the bank, I imagine I would accummulate the part-payments in the overdraft account until its equal to the outstanding principal amount, then just hand over the overdraft funds to the bank for foreclosure.&lt;/p&gt;

&lt;h2&gt; In closing &lt;/h2&gt;

&lt;p&gt;I believe I have made good use of the account so far. I keep a few of my funds parked there - emergency fund; periodic expenses (non-monthly) fund; one-off expenses (large expense, vacation, etc.) fund.&lt;/p&gt;

&lt;p&gt;I have only just wrapped my head around the nature of part-payments in Max Gain. Once I become comfortable with the idea, I might even employ it.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2026/sbi-max-gain"/>
      <summary type="html">When the overdraft balance and the principal amount balance of my Max Gain account no longer matched the expected numbers, I decided to dig into the nature of the SBI Max Gain account.</summary>
      <category term="Life maintenance" label="Life maintenance" />
      <published>2026-03-10T02:53:56.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:859a810b-58bc-47b2-a8f2-f3d8a5a87574</id>
      <title type="html">A primer on car insurance (in India)</title>
      <updated>2025-12-27T20:58:56.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;h2 id=&quot;the-concept-of-car-insurance&quot;&gt;The concept of car insurance&lt;/h2&gt;

&lt;p&gt;Suppose a mishap occurs that involves your car and someone else. One or more of the following unfortunate outcomes can happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Someone&#39;s property is damaged.&lt;/li&gt;
&lt;li&gt;Someone is harmed, physically or lethally.&lt;/li&gt;
&lt;li&gt;Your car is damaged, reparably or not.&lt;/li&gt;
&lt;li&gt;You or your passengers are harmed, physically or lethally.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The concept of car insurance aims to alleviate the first three outcomes. For the fourth, a proper health and life insurance should be in place. &lt;/p&gt;

&lt;h2 id=&quot;damage-to-someone-or-their-property&quot;&gt;Damage to someone or their property&lt;/h2&gt;

&lt;p&gt;The someone involved in the mishap could seek compensation for damages caused to them or their property. A &lt;strong&gt;&lt;dfn&gt;third-party policy (TP)&lt;/dfn&gt;&lt;/strong&gt; covers this damage. A TP is a legal requirement in India so that when someone raises a claim, this policy automatically kicks in.&lt;/p&gt;

&lt;aside&gt;
&lt;p&gt;Why the name &lt;em&gt;third-party&lt;/em&gt;? Who is the &lt;em&gt;second-party&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;The name third-party is from the perspective of the insurance provider. According to them, the policyholder is the first party, they (insurance provider) are the second party, while the someone involved in the accident, who also sought compensation, are the third-party.&lt;/p&gt;
&lt;/aside&gt;

&lt;h2 id=&quot;damage-to-own-car&quot;&gt;Damage to own car&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;&lt;dfn&gt;own damage policy&lt;/dfn&gt;&lt;/strong&gt; covers damage to your own car. Fixing a car is an expensive affair. While this coverage is not legally mandated, it makes financial sense to purchase one.&lt;/p&gt;

&lt;p&gt;Much of the insurancebabble orbits the own damage policy.&lt;/p&gt;

&lt;p&gt;There are several ways to get your car damaged - accident resulting in partial damage; accident resulting in irreparable damage, also called &lt;i&gt;total loss&lt;/i&gt;; theft; natural disasters like flood, storm, earthquake, etc.&lt;/p&gt;

&lt;p&gt;A basic own damage policy usually covers the above clauses.&lt;/p&gt;

&lt;p&gt;One can choose to buy &lt;strong&gt;add-ons&lt;/strong&gt; to a basic own damage policy, which provides extra layers of coverage, but also increase the premium. These add-ons are usually offered only for the first 3 to 5 years of a car&#39;s life.&lt;/p&gt;

&lt;h3 id=&quot;zero-depreciation&quot;&gt;Zero depreciation&lt;/h3&gt;

&lt;p&gt;A car is a depreciating asset. The insurer takes this into account, and by default, only pays out at a depreciated rate of the repaired or replaced parts. A &lt;strong&gt;&lt;dfn&gt;zero depreciation add-on&lt;/dfn&gt;&lt;/strong&gt; ensures that the insurer pays out at the full rate.&lt;/p&gt;

&lt;h3 id=&quot;return-to-invoice&quot;&gt;Return to Invoice&lt;/h3&gt;

&lt;p&gt;Depreciation also comes in play in theft or &lt;i&gt;total loss&lt;/i&gt;, when the car has been irreparably damaged. In such an event, the insurer pays out the current market value of the car, i.e., manufacturer price minus cumulative yearly depreciation. This maximum sum set by insurer is called &lt;strong&gt;&lt;dfn&gt;Insured Declared Value&lt;/dfn&gt; (&lt;abbr&gt;IDV&lt;/abbr&gt;)&lt;/strong&gt;. So, a claim of theft or total loss is capped at IDV. Insurers provide a &lt;strong&gt;&lt;dfn&gt;Return to Invoice add-on&lt;/dfn&gt;&lt;/strong&gt; to set the maximum assured value at the original invoice value, including registration cost and road tax.&lt;/p&gt;

&lt;h3 id=&quot;engine-cover&quot;&gt;Engine cover&lt;/h3&gt;

&lt;p&gt;A basic own damage policy also does not cover engine damage caused by water or oil leakage. An &lt;strong&gt;&lt;dfn&gt;Engine Protect add-on&lt;/dfn&gt;&lt;/strong&gt; is required to cover this type of damage. &lt;strong&gt;I am still confused about whether a basic own damage policy would cover any engine damage caused by a flood since it covers damage caused by natural disasters&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;no-claims-bonus&quot;&gt;No Claims Bonus&lt;/h3&gt;

&lt;p&gt;If you didn&#39;t make use of your own damage policy in a year, you become eligible for a 20-25% deducation on the premium at the time of policy renewal. This is essentially the &lt;strong&gt;&lt;dfn&gt;No Claims Bonus&lt;/dfn&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;comprehensive-policy&quot;&gt;Comprehensive policy&lt;/h2&gt;

&lt;p&gt;You may choose to buy third-party and own damage policies separately, from different insurers. Or you can choose to buy a &lt;strong&gt;&lt;dfn&gt;comprehensive policy&lt;/dfn&gt;&lt;/strong&gt; from an insurer, which includes both of them.&lt;/p&gt;

&lt;h2 id=&quot;how-i-renewed-my-own-damage-policy&quot;&gt;How I renewed my own damage policy&lt;/h2&gt;

&lt;p&gt;My car (Honda Elevate) came with a comprehensive policy issued through the dealership. It was comprised of a 3-year third-party policy and a 1-year own damage policy with the same insurer (Royal Sundaram).&lt;/p&gt;

&lt;p&gt;When time came to renew the own damage policy, I gathered quotes from the dealership for those insurers with which they had a cashless tie-up at their garages. Then I generated quotes for the same parameters from these insurers&#39; official websites, to gauge the commission the dealership would receive.&lt;/p&gt;

&lt;p&gt;To my surprise, the quotes generated from the official website were on par with or even pricier than the quotes from the dealership. So I went back to the dealership, negotiated the premium down a bit further, and purchased a policy from them.&lt;/p&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In closing&lt;/h2&gt;

&lt;p&gt;There are two types of car insurance policies - &lt;i&gt;third-party policy&lt;/i&gt; to cover damage caused to someone else, and &lt;i&gt;own damage policy&lt;/i&gt; to cover damage caused to your own car. The rest are add-ons.&lt;/p&gt;

&lt;p&gt;When you encounter the term &lt;strong&gt;&lt;em&gt;zero dep insurance&lt;/em&gt;&lt;/strong&gt;, keep in mind that &lt;strong&gt; zero dep insurance = own damage policy + zero depreciation add-on&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Also, &lt;strong&gt;comprehensive policy = own damage policy (with or without add-ons) + third-party policy&lt;/strong&gt;.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/car-insurance-primer"/>
      <summary type="html">With a car comes great expenses. One of those is car insurance. I am a novice car driver and owner. So when time came to renew my car insurance, I found myself drowning in insurancespeak. This post is an attempt to distill my understanding of car insurance for my future self.</summary>
      <category term="Life maintenance" label="Life maintenance" />
      <published>2025-12-27T20:58:56.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:1e8d8aab-5d8a-4f1c-ad1c-6f94cb1377d1</id>
      <title type="html">Improved font rendering in Guix</title>
      <updated>2025-11-20T18:43:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;There is a neat trick to make fonts on Linux look thicker and smoother and sharper. Set the following variable in &lt;code&gt;/etc/environment&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;FREETYPE_PROPERTIES=&quot;cff:no-stem-darkening=0 cff:darkening-parameters=500,400,1000,350,1500,325,2000,300 autofitter:no-stem-darkening=0 autofitter:darkening-parameters=500,400,1000,350,1500,325,2000,300&quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;FREETYPE_PROPERTIES&lt;/code&gt; is set in &lt;code&gt;/etc/environment&lt;/code&gt; so that it sticks as a global configuration. You can see the effect in the following screenshots stitched together from before and after applying the configuration:&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/static/images/font-stem-thickening-before-after-browser.png&quot;&gt;
    &lt;figcaption&gt;Left-hand side - fonts in browser before applying the configuration. Right-hand side - fonts after applying the configuration look thicker and sharper.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src=&quot;/static/images/font-stem-thickening-before-after-terminal.png&quot;&gt;
    &lt;figcaption&gt;Left-hand side - fonts in terminal before applying the configuration. Right-hand side - fonts after applying the configuration look thicker and sharper.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I was able to set &lt;code&gt;FREETYPE_PROPERTIES&lt;/code&gt; in Guix with the help of a great tip from &lt;a href=&quot;https://lists.gnu.org/archive/html/help-guix/2020-12/msg00237.html&quot;&gt;Lo Peter&lt;/a&gt; on how to set a global environment variable in a Guix system.&lt;/p&gt;

&lt;p&gt;Here is the final configuration, to be put in the Guix system&#39;s configuration file (I call it &lt;code&gt;os.scm&lt;/code&gt;).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(operating-system
 (services
  (cons*
   (simple-service
    &#39;global-environment-variables
    session-environment-service-type
    &#39;((&quot;FREETYPE_PROPERTIES&quot; . &quot;cff:no-stem-darkening=0 cff:darkening-parameters=500,400,1000,350,1500,325,2000,300 autofitter:no-stem-darkening=0 autofitter:darkening-parameters=500,400,1000,350,1500,325,2000,300&quot;))))))&lt;/code&gt;&lt;/pre&gt;</content>
      <link href="https://bhoot.dev/2025/guix-improved-font-rendering"/>
      <summary type="html">There is a neat trick to make fonts on Linux look thicker and smoother and sharper. Set the following variable in &lt;code&gt;/etc/environment&lt;/code&gt;:</summary>
      <category term="Guix" label="Guix" />
      <published>2025-11-20T18:43:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:2dbf4fe5-f920-4571-9dec-d1703eb63c7e</id>
      <title type="html">&lt;code&gt;git stage&lt;/code&gt; over &lt;code&gt;git add&lt;/code&gt;</title>
      <updated>2025-07-01T05:57:08.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;I&#39;ve come to prefer &lt;code&gt;git stage&lt;/code&gt; to &lt;code&gt;git add&lt;/code&gt;. The former is a synonym of latter, but makes the intent clearer.

&lt;/p&gt;&lt;p&gt;&lt;code&gt;man git-add&lt;/code&gt; says:

&lt;/p&gt;&lt;blockquote&gt;Add file contents to the index&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;man git-stage&lt;/code&gt; says:

&lt;/p&gt;&lt;blockquote&gt;Add file contents to the staging area&lt;/blockquote&gt;

&lt;p&gt;For all purpose, &lt;i&gt;index&lt;/i&gt; in git parlance means &lt;i&gt;staging area&lt;/i&gt; (as far as I know), but the latter language feels more in sync with how git talks about staging/unstaging in &lt;code&gt;git status&lt;/code&gt;.

&lt;/p&gt;&lt;p&gt;But it might just be because I grew up on the staging parlance.

&lt;/p&gt;&lt;p&gt;As usual, feel free to shine a light on any ignorance on my part.
&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/git-stage-over-git-add"/>
      <summary type="html">I&#39;ve come to prefer &lt;code&gt;git stage&lt;/code&gt; to &lt;code&gt;git add&lt;/code&gt;. The former is a synonym of latter, but makes the intent clearer.</summary>
      <category term="Git" label="Git" />
      <published>2025-07-01T05:57:08.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:a9c1d360-42ac-4909-9598-720bccce7166</id>
      <title type="html">On Stephen Baxter&#39;s Manifold books</title>
      <updated>2025-06-20T09:57:32.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;So I finished Stephen Baxter&#39;s &lt;a href=&quot;https://www.goodreads.com/series/297804-world-engines&quot;&gt;World Engines&lt;/a&gt; duology yesterday. I have some thoughts.&lt;/p&gt;

    &lt;p&gt;Setting aside its excruciatingly slow pace, it neatly ties back to Baxter&#39;s another series — the &lt;a href=&quot;https://www.goodreads.com/series/49783-manifold&quot;&gt;Manifold trilogy&lt;/a&gt;.&lt;/p&gt;

    &lt;p&gt;The 3 books of the trilogy are not sequential, but only thematically connected.&lt;/p&gt;

    &lt;p&gt;Each book attempts to resolve Fermi&#39;s paradox — we live around one septillion (24 zeroes after 1) stars. So
        its highly likely that the intelligent alien life somewhere. Then why haven&#39;t we come across some evidence for
        it?&lt;/p&gt;

    &lt;p&gt;The first book — &lt;a href=&quot;https://www.goodreads.com/book/show/63742.Time&quot;&gt;Manifold: Time&lt;/a&gt; — concludes that we are truly alone. Humanity and its creations live
        on until the death of the universe, but never finds other intelligent lifeforms. Life on Earth is a rarest of
        the rare exception (which, by the way, we seem to be squandering instead of cherishing). Pretty dour end.&lt;/p&gt;

    &lt;p&gt;The second book — &lt;a href=&quot;https://www.goodreads.com/book/show/63818.Space&quot;&gt;Manifold: Space&lt;/a&gt; — hypothesises that intelligent life is actually everywhere, but
        regular cosmic—level catastrophes wipes a civilisation out before they get to live long enough to come
        across another celestial civilisation.&lt;/p&gt;

    &lt;p&gt;The third book — &lt;a href=&quot;https://www.goodreads.com/book/show/64175.Manifold&quot;&gt;Manifold: Origin&lt;/a&gt; — digs deep into the term &lt;i&gt;Manifold&lt;/i&gt;, and veers into multiverse. It
        suggests that humans and their creations are indeed the only intelligent lifeforms, but we are segregated across
        different universes, so can&#39;t find each other without divine intervention from our advanced future selves.&lt;/p&gt;

    &lt;p&gt;Apparently not content with the conclusion, Baxter wrote the World Engines duology, where he extrapolates the first
        and third books, Time and Origin. Our descendants, close to the death of the universe, are determined to do
        something about us being the only intelligent lifeforms in the universe. They reach back through time to reshape
        the universe into a multiverse, thus allowing creation of several Earths. Then they seed these Earths with the
        original Earth&#39;s life, thus allowing life to evolve on diverse paths.&lt;/p&gt;

    &lt;p&gt;Such a grand concept, muted by weak characters and uninteresting and slow writing.&lt;/p&gt;

    &lt;p&gt;I think its time for me to take a break from Baxter. I cherish his &lt;a href=&quot;https://www.goodreads.com/book/show/2111634.Flood?ref=nav_sb_noss_l_13&quot;&gt;Flood&lt;/a&gt;, &lt;a href=&quot;https://www.goodreads.com/book/show/2111628.Ark?ref=nav_sb_noss_l_10&quot;&gt;Ark&lt;/a&gt;, &lt;a href=&quot;https://www.goodreads.com/book/show/25123838-landfall?ref=nav_sb_ss_1_14&quot;&gt;Landfall&lt;/a&gt; trilogy. I think his &lt;a href=&quot;https://www.goodreads.com/book/show/66792.Evolution&quot;&gt;Evolution&lt;/a&gt; should be a part of our school curriculum. But I cannot, for the life of me, slog through his recent novels anymore.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/stephen-baxter-manifold"/>
      <summary type="html">So I finished Stephen Baxter&#39;s &lt;a href=&quot;https://www.goodreads.com/series/297804-world-engines&quot;&gt;World Engines&lt;/a&gt; duology yesterday. I have some thoughts.</summary>
      <category term="Books" label="Books" />
      <published>2025-06-20T09:57:32.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:6bd4c2e3-ce4d-47c1-afa1-61cca1d51df5</id>
      <title type="html">Tip for my future self: If Emacs throws an arcane error — &lt;code&gt;project--read-file-name: Wrong type argument: stringp, nil&lt;/code&gt; — while trying to find a file in a git project (&lt;code&gt;C-x p f&lt;/code&gt;), then one likely reason is that the git project is empty.</title>
      <updated>2025-05-15T07:26:35.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Tip for my future self: If Emacs throws an arcane error — &lt;code&gt;project--read-file-name: Wrong type argument: stringp, nil&lt;/code&gt; — while trying to find a file in a git project (&lt;code&gt;C-x p f&lt;/code&gt;), then one likely reason is that the git project is empty.&lt;/p&gt;

  &lt;p&gt;Solution? Make sure you have something inside the git project. Even a staged file is enough.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/emacs-fit-at-empty-git-project"/>
      <summary type="html">Tip for my future self: If Emacs throws an arcane error — &lt;code&gt;project--read-file-name: Wrong type argument: stringp, nil&lt;/code&gt; — while trying to find a file in a git project (&lt;code&gt;C-x p f&lt;/code&gt;), then one likely reason is that the git project is empty.</summary>
      <category term="Emacs" label="Emacs" />
<category term="Note" label="Note" />
      <published>2025-05-15T07:26:35.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:8817051d-2e10-41af-93a0-dde98191db22</id>
      <title type="html">How I configured OS-specific fonts in Emacs</title>
      <updated>2025-04-22T19:40:38.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Here is the &lt;i&gt;Elisp&lt;/i&gt; snippet:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(let ((font-value (cond
                   ((eq system-type &#39;gnu/linux)
                    &quot;Source Code Pro-12:weight=medium&quot;)
                   ((eq system-type &#39;darwin)
                    &quot;Source Code Pro-17:weight=medium&quot;))))
  (add-to-list &#39;default-frame-alist `(font . ,font-value)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Things I learned while creating this snippet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;system-type&lt;/code&gt; variable in Elisp identifies the operating system.&lt;/li&gt;

&lt;li&gt;A &lt;i&gt;quote&lt;/i&gt; &lt;code&gt;&#39;&lt;/code&gt; does not evaluate the entire quoted expression, but a &lt;i&gt;backquote&lt;/i&gt; &lt;code&gt;`&lt;/code&gt; &lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Backquote.html&quot;&gt;allows evaluation of sub-expressions&lt;/a&gt; marked with a &lt;i&gt;comma&lt;/i&gt; &lt;code&gt;,&lt;/code&gt;, inside the quoted expression. Hence, &lt;code&gt;`(font . ,font-value)&lt;/code&gt;, where &lt;code&gt;font-value&lt;/code&gt; is evaluated to the font value it got in its &lt;i&gt;let binding&lt;/i&gt;, before the parent expression is quoted.&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The syntax to configure font properties. When I last tried Emacs more than 5 years ago (or more), I was stumped by a plethora of solutions on the internet to specify font properties, none of which had worked. Part of why I had abandoned Emacs back then. This time, I was able to figure it out, thanks to the &lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/emacs/Fonts.html&quot;&gt;official doc that describes the syntax to specify a font&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I am sure there is a better way, but I have only recently started tinkering with Emacs and ELisp. I mean I still nest a &lt;i&gt;sexp&lt;/i&gt; by manually adding parentheses before and after the &lt;i&gt;sexp&lt;/i&gt;, like a caveman!&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/emacs-font-per-os"/>
      <summary type="html">Here is the &lt;i&gt;Elisp&lt;/i&gt; snippet:</summary>
      <category term="emacs" label="emacs" />
      <published>2025-04-22T19:40:38.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:ca9a7210-3a69-4623-b308-17ec68d921b9</id>
      <title type="html">A case of unsoundness in TypeScript</title>
      <updated>2025-03-26T10:08:33.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;TypeScript provides a construct called a &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/functions.html#call-signatures&quot;&gt;&lt;i&gt;call signature&lt;/i&gt;&lt;/a&gt; to annotate a callable object with properties in JavaScript. For example, think of a function &lt;code&gt;identity&lt;/code&gt;, i.e., a callable object, with a property &lt;code&gt;whoAmI&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function identity(arg) {
  return arg;
}
identity.whoAmI = &quot;I am Identity Man&quot;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that the above callable construct requires two statements to construct itself. Now, let&#39;s use TypeScript&#39;s call signature feature to annotate it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;interface IIdentity {
  (arg: number) : number
  whoAmI: string
}

const i : IIdentity = (arg) =&amp;gt; arg;
// GOOD: compilation error until the following line is added.
i.whoAmI = &quot;I am Identity!&quot;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So far, so good. However, the following code also type-checks without any compilation errors:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;interface IIdentity {
  (arg: number) : number
  whoAmI: string
}

const i : IIdentity = (arg) =&amp;gt; arg;
// BAD: the following line compiles even if the callable&#39;s construction isn&#39;t finished yet.
console.log(i.whoAmI.toLowerCase());
i.whoAmI = &quot;I am Identity!&quot;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above code of course throws a runtime error: &lt;code&gt;i.whoAmI is undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I don&#39;t see a reason why TypeScript cannot ensure that any reference to the non-existent components of a half-constructed callable is illegal until the callable is fully constructed...except for performance reasons. Performance is also why TypeScript does not infer paramter types of a function automatically even though it lets you infer their types, individually, on-demand, through IDE tooling.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://lobste.rs/s/tjhvij/understanding_various_syntaxes#c_kvjlso&quot;&gt;Link to the conversation thread that led to this post.&lt;/a&gt;&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/typescript-usecase-over-soundness"/>
      <summary type="html">Defining a function declaration with properties requires more than one statement. This provides a way to break the type system of TypeScript.</summary>
      <category term="TypeScript" label="TypeScript" />
      <published>2025-03-26T10:08:33.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:ec62eb12-2d90-4b29-9d7d-57774f038d8c</id>
      <title type="html">Understanding various syntaxes to annotate a function&#39;s type in TypeScript</title>
      <updated>2025-03-21T10:34:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;h2 id=&quot;the-underlying-principles&quot;&gt;The underlying principles&lt;/h2&gt;

&lt;p&gt;I was able to boil it all down to the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Either individually annotate the function parameters and its return value with their respective types; or&lt;/li&gt;
  &lt;li&gt;annotate the &lt;em&gt;variable&lt;/em&gt; that holds a function with the function&#39;s type.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;detour---ways-to-define-a-function-in-javascript&quot;&gt;Detour - ways to define a function in JavaScript&lt;/h2&gt;

&lt;p&gt;Here is a probably non-exhaustive list.&lt;/p&gt;


&lt;h3 id=&quot;function-declaration&quot;&gt;Function declaration&lt;/h3&gt;

&lt;p&gt;This form requires that a name be given to the function.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function identity(v) {
  return v;
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;function-expression&quot;&gt;Function expression&lt;/h3&gt;

&lt;p&gt;This form resolves to a function value which must then be bound to a variable, or must be used immediately, for example, as a callback.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// as a callback
[1, 2, 3].map(&lt;i style=&quot;color: yellow;&quot;&gt;function (v) {
  return v;
}&lt;/i&gt;);

// assigned to a variable
const identity = &lt;i style=&quot;color: yellow;&quot;&gt;function(v) {
  return v;
}&lt;/i&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An anonymous (or orphan(?)) function expression - without being used as a callback or assigned to a variable - is illegal at the top level, because it cannot be distinguished from a function declaration. So the following form is &lt;em&gt;illegal&lt;/em&gt; at the top level:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt; function (v) {
  return v;
}
&lt;samp style=&quot;color: red;&quot;&gt;Uncaught SyntaxError: function statement requires a name&lt;/samp&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A function expression can be named too.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;const identity = function id(v) {
  return v;
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;arrow-function-expression&quot;&gt;Arrow function expression&lt;/h3&gt;

&lt;p&gt;Unlike its function expression counterpart, an anonymous arrow function expression is legal, but useless, at the top level.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt; (v) =&amp;gt; {
  return v;
}
// Console output:
&lt;samp&gt;function (v)
  length: 1
  name: &quot;&quot;
  // ... &lt;/samp&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An anonymous arrow function expression is best used &lt;strong&gt;as a callback&lt;/strong&gt;.&lt;/p&gt;

&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;code&gt;[1, 2, 3].map(&lt;i style=&quot;color: yellow;&quot;&gt;(v) =&amp;gt; {
  return v;
}&lt;/i&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An arrow function expression can be &lt;strong&gt;assigned to a variable&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const identity = v =&amp;gt; v&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;syntax--1---annotate-the-function-parameters-and-its-return-value&quot;&gt;Syntax #1 - annotate the function parameters and its return value&lt;/h2&gt;

&lt;p&gt;Most straightforward. All of the function forms can be annotated with this approach. But a &lt;strong&gt;function declaration can be annotated only through this approach&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Remembering the position of type annotations of the parameters is easy enough. But the &lt;strong&gt;return value type is placed between the parameter list and the body of the function&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For a function declaration and function expression, this means that the return type is placed between the parameter list and the opening brace &lt;code&gt;{&lt;/code&gt; of the function body. For an arrow function, it is placed between the parameter list and the arrow &lt;code&gt;=&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// function declaration
function identity (v&lt;i style=&quot;color: yellow;&quot;&gt;: number&lt;/i&gt;)&lt;i style=&quot;color: yellow;&quot;&gt;: number&lt;/i&gt; {
  return v;
}

// function expression
[1, 2, 3].map(function (v&lt;i style=&quot;color: yellow;&quot;&gt;: number&lt;/i&gt;)&lt;i style=&quot;color: yellow;&quot;&gt;: number&lt;/i&gt; {
  return v;
});

// arrow function expression
const identity = (v&lt;i style=&quot;color: yellow;&quot;&gt;: number&lt;/i&gt;)&lt;i style=&quot;color: yellow;&quot;&gt;: number&lt;/i&gt; =&amp;gt; {
  return v;
}

// arrow function expression as a callback
[1, 2, 3].map((v&lt;i style=&quot;color: yellow;&quot;&gt;: number&lt;/i&gt;)&lt;i style=&quot;color: yellow;&quot;&gt;: number&lt;/i&gt; =&amp;gt; {
  return v;
});&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;syntax--2---annotate-the-variable-that-holds-a-function-expression&quot;&gt;Syntax #2 - Annotate the variable that holds a function expression&lt;/h2&gt;

&lt;p&gt;In this approach, simply annotate the variable name just like any other variable. Only that you have to annotate it with the type of the function expression.&lt;/p&gt;

&lt;p&gt;This format is called &lt;strong&gt;function type expression&lt;/strong&gt;, because they are used to type a function expression (and not a function declaration).&lt;/p&gt;

&lt;p&gt;The format of a function type expression is confusingly similar to that of an arrow function expression: &lt;code&gt;(param: type) =&amp;gt; return_type&lt;/code&gt;.&lt;/p&gt;

&lt;aside&gt;Unintuitively, in a function type expression, parameter names are necessary, but not their types - &lt;code&gt;any&lt;/code&gt; type is assigned by default.&lt;/aside&gt;

&lt;p&gt;For a JavaScript function expression:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;const identity = v =&amp;gt; v

// or
const identity = function(v) {
  return v;
}&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;annotate it by simply sticking the type expression between the variable name &lt;code&gt;identity&lt;/code&gt; and the &lt;code&gt;=&lt;/code&gt; preceding the function body:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;const identity&lt;i style=&quot;color: yellow;&quot;&gt;: (v: number) =&amp;gt; number&lt;/i&gt; = v =&amp;gt; v

// or
const identity&lt;i style=&quot;color: yellow;&quot;&gt;: (v: number) =&amp;gt; number&lt;/i&gt; = function(v) {
  return v;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As I said before, a function type expression worsens readability because it is confusingly similar to an arrow function expression. We can separate the function type expression by defining it as a type:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;type identityFn = &lt;i style=&quot;color: yellow;&quot;&gt;(v: number) =&amp;gt; number&lt;/i&gt;;

const identity&lt;i style=&quot;color: yellow;&quot;&gt;: identityFn&lt;/i&gt; = v =&amp;gt; v;

// or
const identity&lt;i style=&quot;color: yellow;&quot;&gt;: identityFn&lt;/i&gt; = function(v) {
  return v;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, this looks similar to any other variable annotation: &lt;code&gt;const a: [string, number] = [&quot;hello&quot;, 1]&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;syntax--3---annotate-a-function-expression-as-an-object-type&quot;&gt;Syntax #3 - annotate a function expression as an object type&lt;/h2&gt;

&lt;p&gt;More of an edge case, but worth distinguishing it from the rest.&lt;/p&gt;

&lt;p&gt;A function is an object and can have properties. If you want to type a function as an object type, then you have to type the callable function with a format called &lt;strong&gt;call signature&lt;/strong&gt; inside the object type. This format is similar to how you would annotate a function declaration&#39;s parameters and its return type.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;type IIdentity = {
  // call signature
  &lt;i style=&quot;color: yellow;&quot;&gt;(v: number): number&lt;/i&gt;,
  // other properties, if any
  whoAmI: &quot;I am identity man&quot;
}

// or
interface IIdentity {
  // call signature
  &lt;i style=&quot;color: yellow;&quot;&gt;(v: number): number&lt;/i&gt;,
  // other properties, if any
  whoAmI: &quot;I am identity man&quot;
}

const identity&lt;i style=&quot;color: yellow;&quot;&gt;: IIdentity&lt;/i&gt; = v =&amp;gt; v;
identity.whoAmI = &quot;I am identity man&quot;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This syntax leaves room to create an unsound use case, as &lt;a href=&quot;/2025/typescript-usecase-over-soundness/&quot;&gt;explained in a later post&lt;/a&gt;.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/typescript-function-type-syntaxes"/>
      <summary type="html">I often found myself second guessing while writing type annotations for TypeScript functions. To make it intuitive in future, I try to make sense of various syntaxes TypeScript provides in order to annotate functions.</summary>
      <category term="TypeScript" label="TypeScript" />
      <published>2025-03-21T10:34:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:f13619df-46e0-4bac-8ca0-25497a183534</id>
      <title type="html">JavaScript function declaration v/s arrow function in a browser console</title>
      <updated>2025-03-20T13:33:12.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;I often like to develop and test small JavaScript code directly in a browser console.&lt;/p&gt;

&lt;p&gt;In this environment, a function declaration has an advantage over an arrow function – I can re-define &amp;amp; re-run a function declaration repeatedly in a browser&#39;s console.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt; function strIdentity(v) {
  return v.toUpperCase();
}
&lt;samp&gt;undefined&lt;/samp&gt;

&amp;gt;&amp;gt; function strIdentity(v) {
  return v.toLowerCase();
}
&lt;samp&gt;undefined&lt;/samp&gt;

&amp;gt;&amp;gt; strIdentity(&quot;HELLO&quot;);
&quot;hello&quot; &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On the other hand, re-running an arrow function, which have to be assigned to a variable to be usable at the top level, would throw a re-declaration error, because the same variable cannot be re-declared again.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt; const strIdentity = v =&amp;gt; v.toLowerCase();
&lt;samp&gt;undefined&lt;/samp&gt;

&amp;gt;&amp;gt; // editing the above script

&amp;gt;&amp;gt; const strIdentity = v =&amp;gt; v.toUpperCase();
&lt;samp style=&quot;color: red;&quot;&gt;Uncaught SyntaxError: redeclaration of const strIdentity&lt;/samp&gt;&lt;/code&gt;&lt;/pre&gt;</content>
      <link href="https://bhoot.dev/2025/function-vs-arrow"/>
      <summary type="html">Apart from the usual conciseness and &lt;code&gt;this&lt;/code&gt; arguments, JavaScript&#39;s function declaration and arrow functions differ in another practical way.</summary>
      <category term="JavaScript" label="JavaScript" />
      <published>2025-03-20T13:33:12.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:efdac3b9-aaec-4f39-8629-1ebf5c8af173</id>
      <title type="html">Someone&#39;s high hopes- to dash or not to dash</title>
      <updated>2025-03-06T12:13:15.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Someone close to you is brimming with optimism – that something they are anticipating is going to transpire in their favour. They have rationalised all related events to point to their success. Be it a test, an interview,  a pregnancy test, or whatever.&lt;/p&gt;

&lt;p&gt;You, having witnessed several such cycles of hope and disappointment by now, feel an urge – an urge as high as their high hopes – to temper this optimism. What do you do?&lt;/p&gt;

&lt;h2&gt;A matrix of emotions&lt;/h2&gt;

&lt;p&gt;Let&#39;s try to chart the state of their emotions in different scenarios.&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;How do you handle their high hopes?&lt;/th&gt;
&lt;th&gt;Their emotional state before the event&lt;/th&gt;
&lt;th&gt;Event transpires&lt;/th&gt;
&lt;th&gt;Their emotional state after the event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td style=&quot;background-color: orange;&quot;&gt;You try to temper their hopes&lt;/td&gt;
&lt;td style=&quot;background-color: orange;&quot;&gt;They are clouded with doubt, feel unsupported&lt;/td&gt;
&lt;td style=&quot;background-color: green; color: white;&quot;&gt;They succeed&lt;/td&gt;
&lt;td style=&quot;background-color: green; color: white;&quot;&gt;They feel joyful, content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td style=&quot;background-color: green; color: white;&quot;&gt;You stay hopeful too&lt;/td&gt;
&lt;td style=&quot;background-color: green; color: white;&quot;&gt;They feel hopeful and optimistic&lt;/td&gt;
&lt;td style=&quot;background-color: green; color: white;&quot;&gt;They succeed&lt;/td&gt;
&lt;td style=&quot;background-color: green; color: white;&quot;&gt;They feel joyful, content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td style=&quot;background-color: orange;&quot;&gt;You try to temper their hopes&lt;/td&gt;
&lt;td style=&quot;background-color: orange;&quot;&gt;They are clouded with doubt, feel unsupported&lt;/td&gt;
&lt;td style=&quot;background-color: orange;&quot;&gt;They fail&lt;/td&gt;
&lt;td style=&quot;background-color: orange;&quot;&gt;They are disappointed, potentially resentful due to perceived lack of support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td style=&quot;background-color: green; color: white;&quot;&gt;You stay hopeful too&lt;/td&gt;
&lt;td style=&quot;background-color: green; color: white;&quot;&gt;They feel hopeful and optimistic&lt;/td&gt;
&lt;td style=&quot;background-color: orange;&quot;&gt;They fail&lt;/td&gt;
&lt;td style=&quot;background-color: orange;&quot;&gt;They are disappointed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The point here is that, in the cases where they fail, the intensity of their disappointment would probably remain the same, whether or not you had tried to temper their hopes. So why not let them have their positive state of mind for a longer period?&lt;/p&gt;

&lt;p&gt;The catch is that you can&#39;t just look hopeful for the sake of it. You have to &lt;em&gt;feel&lt;/em&gt; it. Otherwise, having had to hide your true feelings, you run the risk of feeling frustrated yourself!&lt;/p&gt;

&lt;p&gt;Of course, the worst case is that you are overcome with the sentiment of &lt;em&gt;I told you so&lt;/em&gt;.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/high-hopes-to-dash-or-not"/>
      <summary type="html">That is the question.</summary>
      <category term="Philosophy of life" label="Philosophy of life" />
      <published>2025-03-06T12:13:15.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:87e3da7a-67f9-465e-bed1-261e46774e4b</id>
      <title type="html">Copying &lt;em&gt;all&lt;/em&gt; files of a directory, including hidden ones, with &lt;code&gt;cp&lt;/code&gt;</title>
      <updated>2025-02-20T17:15:58.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;There are two directories - a non-empty &lt;code&gt;src&lt;/code&gt; and an empty &lt;code&gt;dest&lt;/code&gt;. My &lt;strong&gt;aim&lt;/strong&gt; is to copy all the files in &lt;code&gt;src&lt;/code&gt; inside &lt;code&gt;dest&lt;/code&gt;, including hidden files.&lt;/p&gt;

&lt;figure&gt;
&lt;pre&gt;&lt;code&gt;$ ls -a src&lt;/code&gt;
&lt;samp&gt;.  ..  .hidden  unhidden&lt;/samp&gt;

&lt;code&gt;$ ls -a dest&lt;/code&gt;
&lt;samp&gt;. ..&lt;/samp&gt;
&lt;/pre&gt;
&lt;figcaption&gt;&lt;code&gt;src&lt;/code&gt; dir contains a normal file named &lt;code&gt;unhidden&lt;/code&gt; and a hidden file called &lt;code&gt;.hidden&lt;/code&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;solution---cp--r-src---dest&quot;&gt;Solution - &lt;code&gt;cp -R src/. dest&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;I want to cut to the chase, so here is the command: &lt;code&gt;&lt;strong&gt;cp -R &lt;strong style=&quot;color: red;&quot;&gt;src/.&lt;/strong&gt; dest&lt;/strong&gt;&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;pre&gt;&lt;code&gt;$ cp -R src/. dest&lt;/code&gt;

&lt;code&gt;$ ls -a dest&lt;/code&gt;
&lt;samp&gt;. .. .hidden unhidden&lt;/samp&gt;
&lt;/pre&gt;
&lt;figcaption&gt;The &lt;code&gt;src/.&lt;/code&gt; construct performs the magic.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I was more interested in figuring out why &lt;code&gt;src/.&lt;/code&gt; makes this happen. It felt counter-intuitive to me – &lt;code&gt;src/.&lt;/code&gt; simply refers to the &lt;code&gt;.&lt;/code&gt; entry in the &lt;code&gt;src&lt;/code&gt; directory, i.e., it refers to  &lt;code&gt;src&lt;/code&gt; itself. A command like &lt;code&gt;cp -R src dest&lt;/code&gt; should have copied the entire &lt;code&gt;src&lt;/code&gt; directory inside &lt;code&gt;dest&lt;/code&gt;, ending up with a structure like &lt;code&gt;dest/src/{.hidden,unhidden}&lt;/code&gt; instead of &lt;code&gt;dest/{.hidden,unhidden}&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;why-does-it-work-&quot;&gt;Why does it work?&lt;/h2&gt;

&lt;p&gt;After some faffing about, I found the algorithm &lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cp.html&quot;&gt;used by &lt;code&gt;cp&lt;/code&gt; as documented in POSIX&lt;/a&gt;. Below is the relevant portion of the algorithm.&lt;/p&gt;

&lt;blockquote cite=&quot;https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cp.html&quot;&gt;
&lt;code&gt; cp -R source_file target&lt;/code&gt;

&lt;p&gt;If &lt;code&gt;target&lt;/code&gt; exists and names an existing directory, the name of the corresponding destination path for each file in the file hierarchy rooted in each &lt;em&gt;source_file&lt;/em&gt; shall be the &lt;strong&gt;concatenation&lt;/strong&gt; of &lt;code&gt;target&lt;/code&gt;, a single &amp;gt;slash&amp;lt; character if &lt;code&gt;target&lt;/code&gt; did not end in a &amp;gt;slash&amp;lt;, and the &lt;strong&gt;pathname of the file relative to the directory&lt;/strong&gt; containing &lt;code&gt;source_file&lt;/code&gt;. [Emphasis added]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I tried to understand the algorithm by building path for &lt;code&gt;src/.hidden&lt;/code&gt; file. According to the algorithm, the path of a target file is a concatenation of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;em&gt;target&lt;/em&gt; (&lt;code&gt;dest&lt;/code&gt; in our case) +&lt;/li&gt;
&lt;li&gt;&lt;em&gt;/&lt;/em&gt; if needed (which we do) + &lt;/li&gt;
&lt;li&gt;relative pathname of the target file, starting from the directory that &lt;em&gt;contains&lt;/em&gt; the &lt;code&gt;source_file&lt;/code&gt;. I am interpreting &lt;em&gt;heavily&lt;/em&gt; now – in our case of &lt;code&gt;src/.&lt;/code&gt;, the directory containing &lt;code&gt;.&lt;/code&gt; is &lt;code&gt;src&lt;/code&gt;. So the pathname for the file &lt;code&gt;.hidden&lt;/code&gt; , relative from &lt;code&gt;src&lt;/code&gt;, would become: &lt;code&gt;./.hidden&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thus, the built path for the hidden file &lt;code&gt;.hidden&lt;/code&gt;  becomes: &lt;code&gt;dest/./.hidden&lt;/code&gt;, which is equal to &lt;code&gt;dest/.hidden&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Similarly, for the normal file &lt;code&gt;unhidden&lt;/code&gt;, the built path becomes &lt;code&gt;dest/./unhidden&lt;/code&gt;, which is equal to &lt;code&gt;dest/unhidden&lt;/code&gt;.&lt;/p&gt;

&lt;aside&gt;
&lt;h2 id=&quot;why-not-just-use-cp--r-src---dest--&quot;&gt;Why not just use &lt;code&gt;cp -R src/* dest/&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Because it just wouldn&#39;t do what we want it to. &lt;code&gt;*&lt;/code&gt; is a shell construct called &lt;i&gt;globstar&lt;/i&gt;. bash expands &lt;code&gt;src/*&lt;/code&gt; to all the files inside &lt;code&gt;src&lt;/code&gt;, &lt;em&gt;except the hidden files&lt;/em&gt;, &lt;em&gt;before&lt;/em&gt; running the &lt;code&gt;cp&lt;/code&gt; command. So the final command becomes &lt;code&gt;cp -R src/unhidden dest/&lt;/code&gt;. The hidden file &lt;code&gt;.hidden&lt;/code&gt; is not copied to &lt;code&gt;dest&lt;/code&gt;.&lt;/p&gt;
&lt;/aside&gt;

&lt;aside&gt;
&lt;h2 id=&quot;chatgpt-took-me-for-a-ride&quot;&gt;ChatGPT took me for a ride&lt;/h2&gt;
&lt;p&gt;Take a look at the &lt;a href=&quot;https://chatgpt.com/share/67b77991-e250-8013-8f9e-7107cd48206e&quot;&gt;wild goose chase that ChatGPT&lt;/a&gt; sent me on, even after I asked it to digest the aforelinked POSIX doc.&lt;/p&gt;

&lt;p&gt;In general, I have seen more use for ChatGPT to produce stuff which I have the knowledge and capability to cross-verify. For learning new stuff, original documentation and books are still the king. &lt;/p&gt;
&lt;/aside&gt;</content>
      <link href="https://bhoot.dev/2025/cp-dot-copies-everything"/>
      <summary type="html">I outline my exploration of why &lt;code&gt;cp -R src/. dest&lt;/code&gt; copies contents of &lt;code&gt;src&lt;/code&gt; – that too &lt;em&gt;all&lt;/em&gt; its files, including hidden ones – and not &lt;code&gt;src&lt;/code&gt; itself, as &lt;code&gt;src/.&lt;/code&gt; had initially led me to believe. This is because of how the algorithm of &lt;code&gt;cp&lt;/code&gt; is defined.</summary>
      <category term="Linux" label="Linux" />
<category term="RHCSA" label="RHCSA" />
      <published>2025-02-20T17:15:58.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:8f8baebb-e7b1-4127-b8af-de3d3a944224</id>
      <title type="html">Proximity of an error in static and dynamic languages</title>
      <updated>2025-02-19T14:09:43.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=43092003#43098447&quot;&gt;A thread on Hacker news&lt;/a&gt; got me thinking today. The debate was around type validation. I wrote the following there.&lt;/p&gt;

&lt;blockquote cite=&quot;https://news.ycombinator.com/item?id=43092003#43098447&quot;&gt;
&lt;p&gt;...point is that a bad payload will fail right at the serialisation boundary in case of .NET. We know the problem right there. Now we only need to fix the bad payload.&lt;/p&gt;

&lt;p&gt;For TypeScript with only types and without validation, a bad payload gets through, and there is no telling where in the workflow it will explode. This could waste more time and developer resources in debugging.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In software development, I feel that a runtime error could be judged by two aspects.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How far away is the error thrown from the actual problem?&lt;/li&gt;

&lt;li&gt;If the error is thrown right at the actual problem, or as close as possible to it, then how good the error is at explaining the problem?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my experience, dynamically-typed languages do worse in the first test. Whenever that is a case, the second test becomes moot.&lt;/p&gt;

&lt;p&gt;Statically-typed languages, of course, do relatively better than dynamic languages in the first test. They often fail in the second test though. Newer crop of statically-typed languages like Elm and Rust are trying to improve.&lt;/p&gt;

&lt;h2&gt;An example - a web service&lt;/h2&gt;

&lt;p&gt;Think of a web API. An API endpoint written in a static language would not just define the type of an incoming payload, but would also have to perform a &lt;strong&gt;mandatory&lt;/strong&gt; serialisation. In fact, in most cases, the framework/library being used does the serialisation automatically (through reflection and such).&lt;/p&gt;

&lt;p&gt;In contrast, for an API endpoint written in a dynamic language, not only the type of the incoming payload is absent, but the serialisation is also user&#39;s responsibility. A bolted-on type system like TypeScript can help define the type and the subsequent access of the payload in the code, but it cannot help with the serialisation aspect. A bad payload is a runtime problem, and TypeScript types are long gone by then.&lt;/p&gt;

&lt;p&gt;Consider a web service with two endpoints.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;/GET /increment/:number&lt;/code&gt;, which returns &lt;code&gt;:number + 1&lt;/code&gt;.&lt;/li&gt;

  &lt;li&gt;
    &lt;p&gt;&lt;code&gt;/POST /change-case&lt;/code&gt;, which accepts a JSON payload of the following format.&lt;/p&gt;

    &lt;pre&gt;&lt;code&gt;# Payload Structure
{
  &quot;operation&quot;: &quot;string&quot;,
  &quot;operand&quot;: &quot;string&quot;
}

# Sample 1
{ &quot;operation&quot;: &quot;upper&quot;, &quot;operand&quot;: &quot;Hello&quot;}
# =&amp;gt; should return upper-cased &quot;HELLO&quot;

# Sample 2
{ &quot;operation&quot;: &quot;lower&quot;, &quot;operand&quot;: &quot;woRLD&quot;}
# =&amp;gt; should return lower-cased &quot;world&quot;

# Sample 3
{ &quot;operation&quot;: &quot;invalid&quot;, &quot;operand&quot;: &quot;woRLD&quot;}
# =&amp;gt; should return the original string &quot;woRLD&quot;&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let&#39;s build this service in a statically-typed language. I chose Scala.&lt;/p&gt;

&lt;h2&gt;Scala service&lt;/h2&gt;

&lt;p&gt;I used the tiniest HTTP framework I could find in Scala - &lt;a href=&quot;https://github.com/com-lihaoyi/cask&quot;&gt;Cask&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//&amp;gt; using dep com.lihaoyi::cask:0.10.2

enum Op:
  case Lower, Upper

object MinimalApplication extends cask.MainRoutes {
  @cask.get(&quot;/increment/:num&quot;)
  def getArticle(num: Int) = {
    num + 1
  }

  @cask.postJson(&quot;/change-case&quot;)
  def changeCase(operation: String, operand: String) = {
    operation match
      case &quot;lower&quot; =&amp;gt; operand.toLowerCase()
      case &quot;upper&quot; =&amp;gt; operand.toUpperCase()
      case _ =&amp;gt; operand
  }

  initialize()
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let&#39;s test the endpoints with bad payloads.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# POST /change-case
# Bad payload 1: &quot;operand&quot; is missing.
$ curl -X POST http://localhost:8080/change-case --data &#39;{ &quot;operation&quot;: &quot;lower&quot;}&#39;&lt;/code&gt;

&lt;code style=&quot;color: red;&quot;&gt;# Response is an error
Missing argument: (operand: String)
Arguments provided did not match expected signature:
changeCase
  operation  String
  operand  String
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;# GET /increment/:num
# Bad payload: string instead of an integer
$ curl -X GET http://localhost:8080/increment/yo
&lt;/code&gt;
&lt;code style=&quot;color: red;&quot;&gt;The following argument failed to parse:

num: Int = &quot;List(yo)&quot; failed to parse with java.lang.NumberFormatException: For input string: &quot;yo&quot;
java.lang.NumberFormatException: For input string: &quot;yo&quot;
        at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
        at java.base/java.lang.Integer.parseInt(Integer.java:662)
        at java.base/java.lang.Integer.parseInt(Integer.java:778)
        at scala.collection.StringOps$.toInt$extension(StringOps.scala:910)
        at cask.endpoints.QueryParamReader$.IntParam$$superArg$1$$anonfun$1(WebEndpoints.scala:69)
        at cask.endpoints.QueryParamReader$SimpleParam.read(WebEndpoints.scala:62)
        at cask.endpoints.QueryParamReader$SimpleParam.read(WebEndpoints.scala:62)
        at cask.router.Runtime$.makeReadCall$$anonfun$7(Runtime.scala:45)
        at cask.router.Runtime$.tryEither(Runtime.scala:6)
        at cask.router.Runtime$.makeReadCall(Runtime.scala:45)
        at MinimalApplication$.$anonfun$4$$anonfun$1(main.scala:20)
        at scala.collection.immutable.List.map(List.scala:247)
        at scala.collection.immutable.List.map(List.scala:79)
        at MinimalApplication$.$anonfun$4(main.scala:20)
        at scala.collection.LazyZip3$$anon$9$$anon$10.next(LazyZipOps.scala:171)
        at scala.collection.immutable.List.prependedAll(List.scala:153)
        at scala.collection.immutable.List$.from(List.scala:685)
        at scala.collection.immutable.List$.from(List.scala:682)
        at scala.collection.BuildFromLowPriority2$$anon$11.fromSpecific(BuildFrom.scala:115)
        at scala.collection.BuildFromLowPriority2$$anon$11.fromSpecific(BuildFrom.scala:112)
        at scala.collection.LazyZip3.map(LazyZipOps.scala:165)
        at MinimalApplication$.$anonfun$3(main.scala:20)
        at cask.router.EntryPoint.invoke(EntryPoint.scala:47)
        at cask.router.Decorator$.invoke$$anonfun$2(Decorators.scala:59)
        at cask.endpoints.WebEndpoint.wrapFunction(WebEndpoints.scala:14)
        at cask.endpoints.WebEndpoint.wrapFunction$(WebEndpoints.scala:10)
        at cask.endpoints.get.wrapFunction(WebEndpoints.scala:31)
        at cask.router.Decorator$.invoke(Decorators.scala:52)
        at cask.main.Main$DefaultHandler.handleRequest(Main.scala:123)
        at io.undertow.server.Connectors.executeRootHandler(Connectors.java:395)
        at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:861)
        at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
        at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
        at java.base/java.lang.Thread.run(Thread.java:1583)

expected signature:

getArticle
  num  Int
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the examples above, the error points to the actual problem, passing the first test. Of course, the presentation probably fails the second test.&lt;/p&gt;

&lt;p&gt;Now let&#39;s build the same service using TypeScript.&lt;/p&gt;

&lt;h2&gt;TypeScript service&lt;/h2&gt;

&lt;p&gt;I used the framework most familiar to me - &lt;a href=&quot;https://fastify.dev/&quot;&gt;Fastify&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import Fastify from &#39;fastify&#39;

type Params = {
  num: number;
}

const fastify = Fastify({
  logger: true
})

fastify.get&amp;lt;{ Params: Params }&amp;gt;(&#39;/increment/:num&#39;, async function handler(request, _reply) {
  const params = request.params;
  return params.num + 1;
});

type Op = {
  operation: string;
  operand: string;
}

fastify.post&amp;lt;{ Body: Op }&amp;gt;(&#39;/change-case&#39;, async function handler(request, _reply) {
  const body = request.body;
  switch (body.operation) {
    case &#39;upper&#39;:
      return body.operand.toUpperCase();

    case &#39;lower&#39;:
      return body.operand.toLowerCase();

    default:
      return body.operand;
  }
});

try {
  await fastify.listen({ port: 3000 })
} catch (err) {
  fastify.log.error(err)
  process.exit(1)
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here, the TypeScript types &lt;code&gt;Params&lt;/code&gt; and &lt;code&gt;Op&lt;/code&gt; certainly aid in writing the code, but let&#39;s see how the service deals with bad payloads.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# POST /change-case
# Bad payload: &quot;operand&quot; is missing.
$ curl -X POST \
-H &#39;Content-Type: application/json&#39; \
  http://localhost:3000/change-case \
  --data &#39;{&quot;operation&quot;: &quot;lower&quot;}&#39;
&lt;/code&gt;
&lt;code style=&quot;color: red;&quot;&gt;# Response error
{
  &quot;statusCode&quot;:500,
  &quot;error&quot;:&quot;Internal Server Error&quot;,
  &quot;message&quot;:&quot;Cannot read properties of undefined (reading &#39;toLowerCase&#39;)&quot;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The error we see above is quite removed from the actual problem, i.e., missing operand.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /increment/:num&lt;/code&gt; doesn&#39;t even throw an error, but returns an incorrect value.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# GET /increment/:num
# Bad payload: string instead of an integer
$ curl -X GET http://localhost:3000/increment/hello
&lt;/code&gt;
&lt;code style=&quot;color:red;&quot;&gt;# Incorrect response, no error
hello1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, I know that the TypeScript service is missing the serialisation-cum-validation portion. Even &lt;a href=&quot;https://fastify.dev/docs/latest/Reference/Validation-and-Serialization/#json-schema-support&quot;&gt;Fastify docs recommend serialisation&lt;/a&gt;. But that&#39;s the point - its a &lt;em&gt;recommendation&lt;/em&gt;, its optional. Also, its quite &lt;strong&gt;explicit and verbose&lt;/strong&gt; as seen in the above link.&lt;/p&gt;

&lt;h3&gt;In closing&lt;/h3&gt;

&lt;p&gt;I should note that this post is more a thought vomit than a claim of what is better or worse. When I started writing, I only had the first section - &lt;em&gt;the two tests of an error&lt;/em&gt; - in mind. I am neither glorifying Scala, not bashing TypeScript or JavaScript. I recognise that having to explicitly add serialisation is probably not a dealbreaker. I also recognise that all statically-typed langauges may not even support implicit serialisation (but probably do mandate serialisation).&lt;/p&gt;

&lt;p&gt;That&#39;s all.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/proximity-of-an-error"/>
      <summary type="html">This is a purely conjectural, anecdotal, probably biased post on how statically-typed languages do better than dynamically-typed languages in keeping an error as close to the source issue as possible.</summary>
      <category term="Philosophy of software design" label="Philosophy of software design" />
      <published>2025-02-19T14:09:43.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:43f46500-f269-4e3b-82e0-e4723df60e63</id>
      <title type="html">Design Rework #1 - reworking my university hoarding</title>
      <updated>2025-02-17T10:14:23.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;h2 id=&quot;original-hoarding&quot;&gt;Original hoarding&lt;/h2&gt;

&lt;p&gt;I found this hoarding while passing by my &lt;a href=&quot;https://www.youtube.com/watch?v=H0ZRwFCkfzU&amp;amp;t=25s&quot;&gt;university - VNSGU&lt;/a&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/static/images/design-rework-1-i-love-vnsgu/original-gate.jpeg&quot;&gt;

&lt;figcaption&gt;The hoarding, on the left hand side of the university gate, lays out a vertically laid-out slogan &lt;b&gt;I am in VNSGU&lt;/b&gt;, and also sports the VNSGU logo, cradled by the vertical slogan.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;problems-in-the-original&quot;&gt;Problems in the original&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No alignment between anything. Even the starting letters of each word of the vertically written slogan are misaligned.&lt;/li&gt;

&lt;li&gt;Lack of focal point. The word &lt;b&gt;am&lt;/b&gt; is needlessly highlighted with a colour.&lt;/li&gt;

&lt;li&gt;Abysmal contrast between text and background&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;rework&quot;&gt;Rework&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Find a strong &lt;strong&gt;alignment&lt;/strong&gt; - between words of the vertically laid-out slogan, as well as between the slogan and the logo.&lt;/p&gt;

&lt;img alt=&quot;Iteration 1 - text and logo are have a strong alignment between them&quot; src=&quot;/static/images/design-rework-1-i-love-vnsgu/rework-1-alignment.svg&quot;&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Find a focal point. &lt;strong&gt;Repeat&lt;/strong&gt; the colour of the logo in this word to highlight the two most important elements - logo and the word &lt;b&gt;VNSGU&lt;/b&gt; &lt;/p&gt;

&lt;img alt=&quot;Iteration 2 - important elements are now highlighted through repetition of logo colour&quot; src=&quot;/static/images/design-rework-1-i-love-vnsgu/rework-2-repetition.svg&quot;&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Use a good &lt;strong&gt;contrast&lt;/strong&gt; of colour between the text and the background.&lt;/p&gt;

&lt;p&gt;I ended up with two variants - the first uses a &lt;strong&gt;complementary&lt;/strong&gt; colour set, where the colour of the logo complements the background colour.&lt;/p&gt;

&lt;img alt=&quot;Final output in complementary colour scheme&quot; src=&quot;/static/images/design-rework-1-i-love-vnsgu/rework-complementary-colours.svg&quot;&gt;

&lt;p&gt;The other uses a &lt;strong&gt;monochromatic&lt;/strong&gt; colour set, where the background colour is set to a tint of the logo colour.&lt;/p&gt;

&lt;img alt=&quot;Final output in monochromatic colour scheme&quot; src=&quot;/static/images/design-rework-1-i-love-vnsgu/rework-monochromatic-colours.svg&quot;&gt;

&lt;/li&gt;

&lt;/ol&gt;

&lt;h2 id=&quot;figma-resources&quot;&gt;Figma resources&lt;/h2&gt;

&lt;p&gt;You can find alternate final designs in my Figma file linked below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.figma.com/design/JTAPsY6LUOumuVMsSV7MBP/I-am-in-VNSGU?node-id=0-1&amp;amp;t=NqvDb2R0cz6F4Ony-1&quot;&gt;Link to the Figma file&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href=&quot;https://www.figma.com/proto/JTAPsY6LUOumuVMsSV7MBP/I-am-in-VNSGU?page-id=0%3A1&amp;amp;node-id=96-90&amp;amp;p=f&amp;amp;viewport=-1968%2C7%2C0.57&amp;amp;t=JByehD4WYiHvZK4u-1&amp;amp;scaling=min-zoom&amp;amp;content-scaling=fixed&amp;amp;starting-point-node-id=96%3A90&quot;&gt;Link to the Figma playable prototype&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In closing&lt;/h2&gt;

&lt;p&gt;Even though the design looks like an improvement to my eyes, both the &lt;a href=&quot;https://palette-tester.9elements.com/?id0=1&amp;amp;color0=%234F55D7&amp;amp;name0=Background&amp;amp;id1=2&amp;amp;color1=%23FF7F27&amp;amp;name1=Logo&amp;amp;mode=both&amp;amp;showAllColors=true&quot;&gt;complementary&lt;/a&gt; and the &lt;a href=&quot;https://palette-tester.9elements.com/?id0=1&amp;amp;color0=%23FFC095&amp;amp;name0=Background&amp;amp;id1=2&amp;amp;color1=%23FF7F27&amp;amp;name1=Logo&amp;amp;mode=both&amp;amp;showAllColors=true&quot;&gt;monochromatic&lt;/a&gt; colour schemes score a &lt;strong&gt;terrible contrast level&lt;/strong&gt; in the &lt;a href=&quot;https://palette-tester.9elements.com/&quot;&gt;Accessible Colour Palette Tester&lt;/a&gt;. I am at a loss about how to resolve it.What I do understand now is that a complementary colour relationship between background and foreground colour doesn&#39;t automatically provide a good contrast.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/ux-design-rework-1-i-love-vnsgu"/>
      <summary type="html">This series is an attempt to learn and practise principles of design by reworking adverts that I find in the real world.</summary>
      <category term="UX/Design" label="UX/Design" />
      <published>2025-02-17T10:14:23.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:27eeadca-a190-4744-a8a2-c5c274257a78</id>
      <title type="html">Making tanstack docs more accessible with Stylus</title>
      <updated>2025-02-17T05:57:43.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">This post adds another Stylus fix to the list &lt;a href=&quot;https://www.wezm.net/v2/posts/2025/stylus/&quot;&gt;written by wezm&lt;/a&gt;.

&lt;p&gt;My mouse/trackpad is configured to auto-click on rest/hover. Over time, I&quot;ve developed a habit of resting the cursor on any white space of a webpage.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;tanstack.com&quot;&gt;Tanstack.com&lt;/a&gt; hosts a collection of headless web widgets to develop web applications. Now, in the &lt;a href=&quot;https://tanstack.com/table/latest/docs/introduction&quot;&gt;tanstack docs&lt;/a&gt;, each heading is a clickable anchor, but the clickable area also spans the whole width of the content area. When I subconsciously rest my cursor on the white space besides a heading, the heading and contents are suddenly yanked to the top.&lt;/p&gt;

&lt;p&gt;Here is the Stylus fix:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;a.anchor-heading {
    display: inline;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, the heading still remains an anchor, but the white space besides the heading text stays out of the clickable area.&lt;/p&gt;

&lt;h2&gt;Update: 19 Feb 2025&lt;/h2&gt;

&lt;p&gt;Tanner Linsley, the founder of the tanstack ecosystem, saw &lt;a href=&quot;https://bsky.app/profile/tannerlinsley.com/post/3lif6c3vlbm2m&quot;&gt;this post on Bluesky&lt;/a&gt;, and prompted me to submit a PR to fix this problem. &lt;a href=&quot;https://github.com/TanStack/tanstack.com/pull/343&quot;&gt;The PR&lt;/a&gt; is now merged.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/stylus-tanstack"/>
      <summary type="html">Stylus is a &lt;a href=&quot;https://add0n.com/stylus.html&quot;&gt;browser extension&lt;/a&gt; with which you can modify styles on a webpage. I use it to fix accessibility and usability niggles faced on my regularly visited webpages.</summary>
      <category term="Stylus" label="Stylus" />
<category term="CSS" label="CSS" />
      <published>2025-02-17T05:57:43.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:7749ee91-b3bd-44a1-9a45-cd69ae779b73</id>
      <title type="html">Use cases for &lt;code&gt;&amp;lt;form method=&quot;dialog&quot;&amp;gt;&lt;/code&gt;</title>
      <updated>2025-01-15T10:52:16.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;I&#39;ve started using a relatively less known value of the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element&#39;s &lt;code&gt;method&lt;/code&gt; attribute, called &lt;code&gt;dialog&lt;/code&gt;, for the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when a form is submitted via AJAX.&lt;/li&gt;

&lt;li&gt;when a form is a multi-step wizard, where each step is a form in itself, and I have to accummulate values from each step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;But what about MDN using it in a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element?&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form&quot;&gt;MDN reference&lt;/a&gt; defines &lt;code&gt;method=&quot;dialog&quot;&lt;/code&gt; as:&lt;/p&gt;

&lt;blockquote cite=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form&quot;&gt;
&lt;p&gt;When the form is inside a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;, closes the dialog and causes a submit event to be fired on submission, without submitting data or clearing the form.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the HTML spec, having been written in officialese and being the more authoritative source than MDN, provides a nice escape hatch.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fs-method&quot;&gt;The spec defines&lt;/a&gt; &lt;code&gt;method=&quot;dialog&quot;&lt;/code&gt; as follows (emphasis mine):&lt;/p&gt;

&lt;blockquote cite=&quot;https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fs-method&quot;&gt;
&lt;p&gt;Indicates the form is intended to close the dialog box in which the form finds itself, &lt;em style=&quot;font-weight: 600;&quot;&gt;if any&lt;/em&gt;, and otherwise not submit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My interpretation is that &lt;code&gt;method=&quot;dialog&quot;&lt;/code&gt; has two jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em class=&quot;font-weight: 600;&quot;&gt;If&lt;/em&gt; a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; using &lt;code&gt;method=&quot;dialog&quot;&lt;/code&gt; is located inside a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; element, then its submission causes the dialog to close.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;method=&quot;dialog&quot;&lt;/code&gt; also does not really submit the form, i.e., it fires the submit event, but leaves the actual submission to further programming, like AJAX. As a nice side-effect, &lt;b&gt;I don&#39;t have to fire an explicit &lt;code&gt;ev.preventDefault()&lt;/code&gt;&lt;/b&gt;.&lt;/li&gt;    
&lt;/ul&gt;

&lt;p&gt;Am I wrong? May be. Please let me know in the comments.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2025/form-method-dialog"/>
      <summary type="html">I&#39;ve started using a relatively less known value of the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element&#39;s &lt;code&gt;method&lt;/code&gt; attribute, called &lt;code&gt;dialog&lt;/code&gt;, for the following scenarios:</summary>
      <category term="HTML" label="HTML" />
      <published>2025-01-15T10:52:16.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:80cea035-27cc-4944-b5fb-8e5b4397153e</id>
      <title type="html">Name your SQL constraints</title>
      <updated>2024-12-09T07:05:18.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;I learnt the hard way, while trying to amend the structure of a database instantiated at more than one place, that its best to explicitly name the SQL constraints like a foreign key.&lt;/p&gt;

&lt;p&gt;Trying to adjust a foreign key constraint by referring it to its implicit name felt like holding on to a falling person just with their shoe. I could never be sure that the implicit name would be identical across different instances of the database.&lt;/p&gt;

&lt;p&gt;Also, in MySQl at least, there was no foolproof way to dynamically query for the implicit name.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/name-your-sql-constraints"/>
      <summary type="html">I learnt the hard way, while trying to amend the structure of a database instantiated at more than one place, that its best to explicitly name the SQL constraints like a foreign key.</summary>
      <category term="SQL" label="SQL" />
      <published>2024-12-09T07:05:18.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:42b89e9a-1aed-412c-89a8-da36a6221c82</id>
      <title type="html">Connecting to MySQL running on NixOS on local network</title>
      <updated>2024-12-07T08:27:23.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;h2 id=&quot;the-big-picture&quot;&gt;The big picture&lt;/h2&gt;

&lt;p&gt;This setup aims to allow connections to a MySQL database hosted on my NixOS workstation, &lt;em&gt;only on local network&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I needed to configure MySQL to listen to connections on network interfaces. I also needed a MySQL user who is authorised to connect from a host on local network. Then, I needed to configure NixOS to open up the port used by MySQL.&lt;/p&gt;

&lt;h2 id=&quot;configure-mysql-to-listen-to-remote-connections&quot;&gt;Configure MySQL to listen to remote connections&lt;/h2&gt;

&lt;p&gt;NixOS features a service to configure MySQL, including the ability to configure MySQL&#39;s &lt;code&gt;my.cnf&lt;/code&gt;. So it took only a few lines to get MySQL up and running.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# in /etc/nixos/configuration.nix
services.mysql = {
  enable = true;

  package = pkgs.mysql84;

  configFile = pkgs.writeText &quot;my.cnf&quot; &#39;&#39;
  [mysqld]
  skip-networking=OFF

  # listen for connections on all network interfaces, though
  # only one interface at firewall level will be opened up.
  bind-address=0.0.0.0
  &#39;&#39;;
};        
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;configure-a-remote-enabled-mysql-user&quot;&gt;Configure a remote-enabled MySQL user&lt;/h2&gt;

&lt;p&gt;I wanted to allow only connections from local network.&lt;/p&gt;

&lt;p&gt;A default command &lt;code&gt;CREATE USER &#39;bhoot&#39;;&lt;/code&gt; would create a user which could connect from any remote host.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.4/en/account-names.html&quot;&gt;MySQL follows&lt;/a&gt; a &lt;code&gt;username@hostname&lt;/code&gt; convention for user accounts, where &lt;code&gt;hostname&lt;/code&gt; can use a &lt;code&gt;host_ip/netmask&lt;/code&gt; syntax to allow connections only from the local network.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
# as root MySQL user
mysql&amp;gt; CREATE USER &#39;bhoot&#39;@&#39;192.168.100.0/255.255.255.0&#39; IDENTIFIED BY &#39;my_password&#39;;
mysql&amp;gt; GRANT ALL ON my_schema.* to &#39;bhoot&#39;@&#39;192.168.100.0/255.255.255.0&#39;;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;configure-nixos-to-open-up-mysql-port&quot;&gt;Configure NixOS to open up MySQL port&lt;/h2&gt;

&lt;p&gt;NixOS enables a firewall by default.&lt;/p&gt;

&lt;p&gt;I was under the (borrowed) impression that NixOS automatically opens up the ports for enabled services. But that wasn&#39;t the case, at least for MySQL.&lt;/p&gt;

&lt;p&gt;Opening up the MySQL port was a matter of a single line:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# in /etc/nixos/configuration.nix
networking.firewall.enable = true;
# Open MySQL port, only on the wireless network interface.
networking.firewall.interfaces.wlp0s20f3.allowedTCPPorts = [ 3306 ];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, rebuild NixOS.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo nixos-rebuild switch --flake .&lt;/code&gt;&lt;/pre&gt;</content>
      <link href="https://bhoot.dev/2024/conn-to-mysql-on-nixos-from-local-network"/>
      <summary type="html">I have a Linux workstation, and a Macbook. While I prefer to work on Linux, I also like the mobility around my house. Once, I needed to access a database instance running on the Linux workstation from my MacBook. My first instinct was to sell it all and buy a Linux-operated ThinkPad! After I placated myself, I came up with the following setup..</summary>
      <category term="MySQL" label="MySQL" />
<category term="Nix" label="Nix" />
      <published>2024-12-07T08:27:23.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:0224c78a-83b9-495d-ba67-892c43e89a4f</id>
      <title type="html">Flossing - do orthodon&#39;t. There is no try.</title>
      <updated>2024-11-19T05:36:29.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Flossing - do orthodon&#39;t. There is no try.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/flossing-do-orthodont"/>
      <summary type="html">Flossing - do orthodon&#39;t. There is no try.</summary>
      <category term="Uncollected" label="Uncollected" />
<category term="Note" label="Note" />
      <published>2024-11-19T05:36:29.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:7031ff09-d4df-4c6b-95bf-ee8de4a9d203</id>
      <title type="html">Being chill</title>
      <updated>2024-11-10T04:40:57.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;One guiding principle to be a chill person is pretty clear to most people: Let go of what you can&#39;t control.&lt;/p&gt;

&lt;p&gt;But, to me, another principle is more important, which also gets overlooked: Don&#39;t ignore what you &lt;em&gt;can&lt;/em&gt; control. Get it done.&lt;/p&gt;

&lt;p&gt;The anxiety, the stress and the guilt that piles up from ignoring the latter is more formidable and &lt;em&gt;insidious&lt;/em&gt; than not being able to practise the former.&lt;/p&gt;

&lt;p&gt;Not doing anything about which you can do something can make you more anxious than not being able to do something about which you can&#39;t do anything.&lt;/p&gt;

&lt;p&gt;Update on&lt;time datetime=&quot;2024-12-13&quot;&gt;13th December 2024&lt;/time&gt;: I thin k that the virtues of patience and perseverance fit somewhere within the get-it-done component. Patience probably also fits in the let-it-go component.&lt;/p&gt;

&lt;h2&gt;Personal experience&lt;/h2&gt;

&lt;p&gt;Procrastinators among the readers of this post, which is me, must have felt hard about the second principle.&lt;/p&gt;

&lt;p&gt;My wife is an avid &#39;get-it-done&#39; person, but frets about stuff out of her control, or which lie in the past or the future.&lt;/p&gt;

&lt;p&gt;On the other hand, I am an avid &#39;will-get-it-done&#39; person, but don&#39;t give a shit about what I can&#39;t control.&lt;/p&gt;

&lt;p&gt;Truly, a pair made in heaven!&lt;/p&gt;

&lt;p&gt;When I ensure that I get done what I &lt;em&gt;can&lt;/em&gt; control, then that sense of accomplishment also helps me put myself at ease about what I can&#39;t control.&lt;/p&gt;

&lt;h3&gt;In closing&lt;/h3&gt;

&lt;p&gt;Being chill is a lot of work! There are days I just prefer to scream and flail around.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/being-chill"/>
      <summary type="html">Being a chill dude is the exact &lt;em&gt;opposite&lt;/em&gt; of being &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Big_Lebowski&quot;&gt;The Dude&lt;/a&gt;.</summary>
      <category term="Philosophy of life" label="Philosophy of life" />
      <published>2024-11-10T04:40:57.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:de9ea10f-6f70-43f3-9a31-69b936ab5b83</id>
      <title type="html">A mental model for Linux file, hard and soft links</title>
      <updated>2024-11-09T10:36:59.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;h2 id=&quot;what-is-a-file-in-linux&quot;&gt;What makes a file in Linux?&lt;/h2&gt;

&lt;p&gt;At the storage level, a file is a block of data. There is more to it than just that, but I won&#39;t delve deep into it.&lt;/p&gt;

&lt;p&gt;At the filesystem level, this block of data is represented as a an abstraction called &lt;strong&gt;&lt;i&gt;inode&lt;/i&gt;&lt;/strong&gt;. Think of inode as a data structure that stores metadata about the underlying data block.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;&lt;i&gt;pathname&lt;/i&gt;&lt;/strong&gt; makes this pair of (inode, data block) accessible and usable for humans.&lt;/p&gt;

&lt;aside&gt;
&lt;h3 id=&quot;what-the-hell-is-a-pathname-&quot;&gt;What the hell is a &lt;em&gt;pathname&lt;/em&gt;?&lt;/h3&gt;

&lt;p&gt;I wanted a single-word term to refer to a &lt;i&gt;filepath+filename&lt;/i&gt; combination. &lt;i&gt;filename&lt;/i&gt; or &lt;i&gt;filepath&lt;/i&gt; didn&#39;t feel like they conveyed the contextual meaning properly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc959&quot;&gt;&lt;i&gt;RFC 959 - File Transfer Protocol&lt;/i&gt;&lt;/a&gt; gave me a decent term - &lt;i&gt;pathname&lt;/i&gt;, defined as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pathname is defined to be the character string which must be input to a file system by a user in order to identify a file. Pathname normally contains device and/or directory names, and file name specification.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/aside&gt;

&lt;p&gt;Think of the whole this way - the data block of a file lies in a lower level of abstraction than the pathname (which lives at the filesystem level). inode acts as a bridge - a representative to the data block at the filesystem level.&lt;/p&gt;

&lt;aside&gt;&lt;p&gt;Man, its difficult to keep a high level overview simple, without diving into details.&lt;/p&gt;&lt;/aside&gt;

&lt;figure&gt;
&lt;img src=&quot;/static/images/on-linux-file-and-links/file-structure.svg&quot;&gt;
&lt;figcaption&gt;Components of a file. From bottom up, data block, inode, and pathname&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Roughly, &lt;strong&gt;file = pathname + inode + data block&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, when you talk about a file, you may be referring to any or all of these components.&lt;/p&gt;

&lt;h2 id=&quot;hard-links&quot;&gt;Hard links&lt;/h2&gt;

&lt;p&gt;Now, &lt;strong&gt;if we think of pathnames as labels slapped on an (inode, data block) pair, then we can refer to the same inode with different labels.&lt;/strong&gt;&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/static/images/on-linux-file-and-links/hard-links.svg&quot;&gt;
&lt;figcaption&gt;Two pathnames &lt;code&gt;/tmp/filename1&lt;/code&gt; and &lt;code&gt;/home/bhoot/filename2&lt;/code&gt; point to the same inode, thus representing two hard links.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In the above figure, &lt;code&gt;/tmp/filename1&lt;/code&gt; and &lt;code&gt;/home/bhoot/filename2&lt;/code&gt; point to the same inode. These two are &lt;i&gt;hard links&lt;/i&gt;. &lt;strong&gt;A &lt;dfn&gt;hard link&lt;/dfn&gt; connects a pathname to an inode.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The above figure can be interpreted in several ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The pathnames point to the inode, not the other way round. Thus, a pathname is aware of the inode, but the inode is not aware of the pathname.&lt;/p&gt;

&lt;aside&gt;
&lt;h3 id=&quot;how-is-a-hard-link-connection-stored-&quot;&gt;How is a hard link connection stored?&lt;/h3&gt;
&lt;p&gt;Its stored in the directory that contains the filename. A directory file is essentially a &quot;table&quot;, where each &quot;row&quot; registers a file contained in the directory. Each row is composed of pathname and a pointer to the inode that it points to. That&#39;s how a pathname is aware of the inode, but not the other way round.&lt;/p&gt;
&lt;/aside&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;At this level of abstraction, is it possible to tell which one of the two pathnames originally created the file? I think not. Hard links are supposed to be equal in status to each other.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;As a corollary to the above point, &lt;strong&gt;there is always at least one hard link attached to a file&lt;/strong&gt;. When you create a file, say, with &lt;code&gt;touch /a/pathname&lt;/code&gt;, you create a hard link between &lt;code&gt;/a/pathname&lt;/code&gt; and the file&#39;s inode.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;deleting-a-hard-link&quot;&gt;Deleting a hard link&lt;/h3&gt;

&lt;p&gt;Refer to the figure above. If you do &lt;code&gt;rm /tmp/filename1&lt;/code&gt;, what should happen from a sane perspective?&lt;/p&gt;

&lt;p&gt;Should the command remove the entire &lt;i&gt;/tmp/filename1 + inode + data block&lt;/i&gt; combo? Then what happens to the other pathname &lt;code&gt;/home/bhoot/filename2&lt;/code&gt; that points to the same inode?&lt;/p&gt;

&lt;p&gt;The right way would be to preserve the integrity of the other pathname &lt;code&gt;/home/bhoot/filename2&lt;/code&gt; while deleting only the targeted pathname &lt;code&gt;/tmp/filename1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That&#39;s what happens when you delete a file - the (hard) link between the &lt;em&gt;specified&lt;/em&gt; pathname and the inode is removed.&lt;/p&gt;

&lt;p&gt;Similarly, &lt;code&gt;mv f1 f2&lt;/code&gt; creates a new hard link &lt;code&gt;f2&lt;/code&gt; &amp;amp; deletes the old hard link &lt;code&gt;f1&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/static/images/on-linux-file-and-links/hard-link-rm.svg&quot;&gt;
    &lt;figcaption&gt;Representation of one of the two hard links, linked to the same inode, being removed. Only the affected pathname is removed. The inode and underlying data block stay in tact.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;What happens when all the hard links to a file are deleted? Now, there are no pathnames in the filesystem that refer to that data block anymore. Thus, the inode is marked for deletion, and the associated data block is orphaned.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/static/images/on-linux-file-and-links/hard-link-rm-all.svg&quot;&gt;
    &lt;figcaption&gt;When all the hard links to an inode are removed, the entire file, along with its content, is removed from the filesystem.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;soft-links&quot;&gt;Soft links&lt;/h2&gt;

&lt;p&gt;Take a look at the following figure.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/static/images/on-linux-file-and-links/soft-links.svg&quot;&gt;
    &lt;figcaption&gt;A regular file identified by the pathname &lt;code&gt;/home/bhoot/filename2&lt;/code&gt; on the left-hand side. A soft link file identified by the pathname &lt;code&gt;filename3&lt;/code&gt; on the right-hand side. The link file points to the regular file. Both the regular and link files have their own inode and their own data block.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The above figure shows a soft link in blue colour (its blue, right?) pointing from the link file &lt;code&gt;filename3&lt;/code&gt; to the target file &lt;code&gt;/home/bhoot/filename2&lt;/code&gt;. &lt;strong&gt;A &lt;dfn&gt;soft link&lt;/dfn&gt; or a &lt;dfn&gt;symbolic link&lt;/dfn&gt; points a soft link file to a target file.&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thus, a soft link links a link file to a target file. This is in contrast to a hard link, which links a pathname to an inode.&lt;/strong&gt;&lt;/p&gt;

&lt;aside&gt;
&lt;p&gt;In the man page of the POSIX &lt;code&gt;ln&lt;/code&gt;, the syntax description goes as: &lt;code&gt;ln [-s] source target&lt;/code&gt;, where the &lt;dfn&gt;source&lt;/dfn&gt; is the file to be linked to, while the &lt;dfn&gt;target&lt;/dfn&gt; is the new link file to be created. These designations may make sense within the specific context of creating a soft link. But, once a soft link is already created, calling it &lt;i&gt;target&lt;/i&gt; doesn&#39;t make sense.&lt;/p&gt;

&lt;p&gt;I prefer the terms used in the man page of the GNU &lt;code&gt;ln&lt;/code&gt;: &lt;code&gt;ln [-s] target link_name&lt;/code&gt;, where &lt;dfn&gt;target&lt;/dfn&gt; is the file to be pointed towards, while &lt;dfn&gt;link_name&lt;/dfn&gt; is the new link file.&lt;/p&gt;
&lt;/aside&gt;

&lt;h3 id=&quot;a-soft-link-is-a-file&quot;&gt;A soft link is a file&lt;/h3&gt;

&lt;p&gt;As we saw in the &lt;a href=&quot;#what-is-a-file-in-linux&quot;&gt;What is a file in Linux?&lt;/a&gt; section, a pathname is always linked to an &lt;i&gt;inode + data block&lt;/i&gt;. This is true in case of a soft link, too. In the above figure, the soft link file &lt;code&gt;filename3&lt;/code&gt;, which points to &lt;code&gt;/home/bhoot/filename2&lt;/code&gt;, also has its own inode &amp;amp; a data block.&lt;/p&gt;

&lt;p&gt;You can check the inode number associated with a pathname with &lt;code&gt;ls -i&lt;/code&gt; command. &lt;strong&gt;Two hard link files pointing to the same inode will print the same inode number, while a soft link file and its target file will print different inode numbers.&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# create a regular file
$ echo &quot;HELLO WORLD HOW ARE YOU?&quot; &amp;gt; file.txt

# create a hard link
$ ln file.txt hardlink.txt

# both hard links refer to the same inode
$ ls -i file.txt hardlink.txt 
&lt;samp&gt;17751620 file.txt  17751620 hardlink.txt&lt;/samp&gt;

# create a soft link
$ ln -s file.txt softlink.txt

# inode of soft link file is different from the target file
$ ls -i file.txt softlink.txt 
&lt;samp&gt;17751620 file.txt  17751663 softlink.txt&lt;/samp&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;contents-of-a-soft-link-file&quot;&gt;Contents of a soft link file&lt;/h3&gt;

&lt;p&gt;How is a soft link relationship stored? In the &lt;em&gt;data block of the soft link file&lt;/em&gt;. &lt;strong&gt;The content of a soft link is the pathname of the target file it points to&lt;/strong&gt;.&lt;/p&gt;
    
&lt;aside&gt;
&lt;s&gt;&lt;p&gt;If a soft link file is created in the same directory as the target file, then only the basename of the target file is stored as the content of the soft link file. Otherwise, the absolute filename of the target file is stored.&lt;/p&gt;&lt;/s&gt;

&lt;p&gt;&lt;time datetime=&quot;2024-11-10T04:24:12Z&quot;&gt;10th of November, 2024:&lt;/time&gt; &lt;a href=&quot;https://lobste.rs/~kosayoda&quot;&gt;Kosayoda on lobste.rs&lt;/a&gt; politely pointed out that my struck-out claim above is bullshit. After a few tests, I concur.&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href=&quot;https://lobste.rs/s/e62p4b/mental_model_for_linux_file_hard_soft#c_gfp3ws&quot;&gt;full explanation&lt;/a&gt;, but a concise summary is: &lt;code&gt;ln -s some-file.txt link-file.txt&lt;/code&gt; simply creates a &lt;code&gt;link-file.txt&lt;/code&gt; file, the content of which is &lt;code&gt;some-file.txt&lt;/code&gt;.&lt;/p&gt;
&lt;/aside&gt;
    
&lt;p&gt;There are 2 ways to verify this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;readlink filename3&lt;/code&gt; prints the content of the soft link &lt;code&gt;filename3&lt;/code&gt;, which is the name of the target pathname.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ readlink filename3&lt;/code&gt;
&lt;samp&gt;/home/bhoot/filename2&lt;/samp&gt;&lt;/pre&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;&lt;code&gt;stat --format=&quot;%s&quot; filename3&lt;/code&gt; prints the size of &lt;code&gt;filename3&lt;/code&gt;, which is equal to the number of characters in the name of &lt;code&gt;/home/bhoot/filename2&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ stat --format=&quot;%s&quot; filename3&lt;/code&gt;
&lt;samp&gt;21&lt;/samp&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;i-o-operations-on-a-soft-link-file&quot;&gt;I/O operations on a soft link file&lt;/h3&gt;

&lt;h4 id=&quot;copying-a-soft-link-file&quot;&gt;Copying a soft link file&lt;/h4&gt;

&lt;p&gt;What happens when you copy a soft link file? &lt;s&gt;Same thing as when a regular file is copied.&lt;/s&gt; Or so I thought.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# create a regular file
$ echo &quot;HELLO WORLD HOW ARE YOU?&quot; &amp;gt; file.txt

# check the content size of target file
$ stat --format=&quot;%s&quot; file.txt
&lt;samp&gt;25&lt;/samp&gt;

# create a soft link
$ ln -s file.txt softlink.txt

# check the size of the soft link file
$ stat --format=&quot;%s&quot; softlink.txt
&lt;samp&gt;8&lt;/samp&gt;

# copy the soft link file
$ cp softlink.txt softlink2.txt

# check the size of the new soft link file, which should be equal to the soft link file
$ stat --format=&quot;%s&quot; softlink2.txt
&lt;samp&gt;25&lt;/samp&gt; # WAT?
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What happened there? The &lt;code&gt;cp&lt;/code&gt; command simply copied the target file instead of the link file.&lt;/p&gt;

&lt;p&gt;&lt;s&gt;&lt;strong&gt;My guess is that when those I/O operations, which involve acting on the &lt;em&gt;data block&lt;/em&gt; of a file, are ran on a soft link file, they are actually executed on the target file that the soft link file is pointed to.&lt;/strong&gt;&lt;/s&gt;&lt;/p&gt;

&lt;p&gt;&lt;s&gt;Thus, &lt;code&gt;cp softlink.txt softlink2.txt&lt;/code&gt; would actually copy the target file. Similarly, &lt;code&gt;cat softlink.txt&lt;/code&gt; would print the content of the target file. But, &lt;code&gt;mv&lt;/code&gt;, which does not need to act on the underlying data block of a file, would just operate on the soft link file itself.&lt;/s&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE: &lt;/strong&gt;When I wrote the couple of struck-out paragraphs above, the purpose was only to have a &lt;i&gt;convenient logic&lt;/i&gt; of memorising how I/O operations work on soft links by default. Memorising techniques need not be technically accurate. However, I have struck it out because the logic is also blatantly inaccurate and has been rightly &lt;a href=&quot;https://news.ycombinator.com/item?id=42099928&quot;&gt;called out by others&lt;/a&gt;. By default, &lt;code&gt;cp&lt;/code&gt; sure works as described above, but &lt;code&gt;cp -P&lt;/code&gt; copies the soft link itself, i.e., the soft link&#39;s inode and data block. &lt;/p&gt;

&lt;h4 id=&quot;moving-a-soft-link-file&quot;&gt;Moving a soft link file&lt;/h4&gt;

&lt;p&gt;What happens when you move a soft link file? This is more predictable. Thankfully, the same as what happens when a regular file is moved - the new pathname entry is created, while the old pathname entry is removed. Because the new pathname still points to the same old inode + data block, it also points to the same target file.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/static/images/on-linux-file-and-links/soft-link-mv.svg&quot;&gt;
    &lt;figcaption&gt;Moving a soft link file replaces the old soft link&#39;s pathname with a new one. The new pathname keeps pointing to the same target file.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;moving-the-target-file&quot;&gt;Moving the target file&lt;/h3&gt;

&lt;p&gt;A more interesting question worth pondering: what happens when the &lt;em&gt;target&lt;/em&gt; file is moved or deleted? Let&#39;s go with a case of severe deletion.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/static/images/on-linux-file-and-links/soft-link-rm-pointee.svg&quot;&gt;
    &lt;figcaption&gt;Moving or deleting a &lt;em&gt;target&lt;/em&gt; file is a more disruptive operation&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;A soft link file depends on its content to determine which file it points to. Even when the target file is deleted, the content of soft link file doesn&#39;t change, i.e., it still keeps pointing to the now deleted target file. With its purpose existing no more, the soft link thus becomes a dangling, dead link.&lt;/p&gt;

&lt;h2 id=&quot;in-closing&quot;&gt;In closing&lt;/h2&gt;

&lt;p&gt;In my mind, the terms hard link and soft link are a bit confusing in that they sound like different tools used for a similar purpose. But, &lt;strong&gt;a hard link exists as a directory entry that links a pathname to an inode, while a soft link exists as a file that links its own pathname to another pathname.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, from the perspective of structural existence, hard links actually look softer than soft links.&lt;/p&gt;

&lt;p&gt;Also, I&#39;m cursing my 8-hours old self for thinking that I could wrap up this post and get it out in 3 to 4 hours.&lt;/p&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further reading&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;a href=&quot;https://www.pixelbeat.org/docs/unix_links.html&quot;&gt;excellent post by Pádraig Brady&lt;/a&gt; also adds &lt;i&gt;reflinks&lt;/i&gt; to this perspective. In this post, I talked about stuff as if a data block is represented by exactly one inode at the filesystem level. But reflinks enable many-to-one mapping between inodes and a data block. &lt;/li&gt;
&lt;li&gt;&lt;code&gt;man 7 inode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;man 7 symlink&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;man 7 path_resolution&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</content>
      <link href="https://bhoot.dev/2024/on-linux-file-and-links"/>
      <summary type="html">I always felt bothered about my superficial understanding of inode, hard and soft links in Linux. Here I attempt to structure my learnings into a simplified mental model. Corrections are welcome.</summary>
      <category term="Mental model" label="Mental model" />
<category term="Linux" label="Linux" />
<category term="RHCSA" label="RHCSA" />
      <published>2024-11-09T10:36:59.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:61505f8c-bb46-4bbe-a3f3-b595b3013351</id>
      <title type="html">The Trumping Joke</title>
      <updated>2024-11-08T16:00:40.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;I was always skeptical about how the &lt;i&gt;Joker&lt;/i&gt; takes over the &lt;i&gt;Mob&lt;/i&gt; in &lt;cite&gt;The Dark Knight&lt;/cite&gt;.&lt;/p&gt;

&lt;p&gt;The Mob looked too powerful, too entrenched for that to happen.&lt;/p&gt;

&lt;p&gt;But they bent over.&lt;/p&gt;

&lt;p&gt;Then, in the real world, Trump came along, took control of the GOP, and then of the whole country. Twice.&lt;/p&gt;

&lt;p&gt;The old guard of the GOP looked too powerful, too entrenched for that to happen.&lt;/p&gt;

&lt;p&gt;But they bent over.&lt;/p&gt;

&lt;p&gt;Both of them whisked away enough followers from the old guard, converted them into fanatics, loyal to their tall claims.&lt;/p&gt;

&lt;p&gt;For every piece of ridiculous fiction, there is a reality out there that outmatches it.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/trump-and-joker"/>
      <summary type="html">I was always skeptical about how the &lt;i&gt;Joker&lt;/i&gt; takes over the &lt;i&gt;Mob&lt;/i&gt; in &lt;cite&gt;The Dark Knight&lt;/cite&gt;.</summary>
      <category term="Uncollected" label="Uncollected" />
      <published>2024-11-08T16:00:40.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:dea8aaf0-4066-43bd-8b01-a5b482609206</id>
      <title type="html">Different kinds of simple</title>
      <updated>2024-10-01T07:22:39.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;This post is syndicated from &lt;a href=&quot;https://lobste.rs/s/8ep75v/how_1_software_engineer_outperforms_138#c_kjyns6&quot;&gt;my response&lt;/a&gt; to a Lobste.rs story titled &lt;a href=&quot;https://lobste.rs/s/8ep75v/how_1_software_engineer_outperforms_138&quot;&gt;How 1 Software Engineer Outperforms 138 - Lichess Case Study&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The video author talked about &lt;em&gt;simple&lt;/em&gt; in terms of fewer lines of code, as in &quot;fewer the features, fewer the lines of code, simpler the codebase&quot;. Recently, I chose another kind of &lt;em&gt;simple&lt;/em&gt; that increased the lines of code and duplication.&lt;/p&gt;

&lt;p&gt;There was a segment implemented in a complex workflow, which contained enough conditionals that I could identify four sub-workflows, which merged back into one at the end of the segment.&lt;/p&gt;

&lt;p&gt;When time came to add more conditionals to the workflow, I had a choice to keep going with a single flow, handling conditionals as and when a branch reared its head. This would keep the lines of code to a minimum. But it would also have made the code difficult to follow to someone who was introduced to it for the first time, or even to someone who revisited it after some time.&lt;/p&gt;

&lt;p&gt;Or I could split that segment into distinct sub-workflows, at the cost of some duplication. More lines of code, but easier to follow. Later on, once I identify the duplicate (i.e., common) components in those sub-workflows, I could extract some of them to reduce the duplication.&lt;/p&gt;

&lt;p&gt;I chose the latter approach. The segment ended up being split into &lt;em&gt;eight&lt;/em&gt; distinct sub-workflows. One clear benefit was that the number of bugs in the existing implementation reduced, because logic unique to each sub-workflow got isolated. For the same reason, the code became easy to follow. I hope this choice remains the right choice further down the line. I feel this approach would be easier to follow to a later re-visitor or to a new person.&lt;/p&gt;

&lt;p&gt;An unaffiliated side-note: the above re-factoring became possible due to the statically typed language the code was implemented in – ReScript, which has an ML-like type system. It gave me a high degree of confidence in making a fearless re-factoring.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/different-kinds-of-simple"/>
      <summary type="html">This post is syndicated from &lt;a href=&quot;https://lobste.rs/s/8ep75v/how_1_software_engineer_outperforms_138#c_kjyns6&quot;&gt;my response&lt;/a&gt; to a Lobste.rs story titled &lt;a href=&quot;https://lobste.rs/s/8ep75v/how_1_software_engineer_outperforms_138&quot;&gt;How 1 Software Engineer Outperforms 138 - Lichess Case Study&lt;/a&gt;.</summary>
      <category term="Philosophy of software design" label="Philosophy of software design" />
      <published>2024-10-01T07:22:39.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:05744722-6bac-4385-b50b-1aa0a802eb53</id>
      <title type="html">Ki modal editor - first impressions</title>
      <updated>2024-09-20T12:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;
      Copy-pasting my comment in a converstion on lobste.rs titled
      &lt;a href=&quot;https://lobste.rs/s/20t1jj/wonderful_vi#c_k7tr1m&quot;&gt;Wonderful vi&lt;/a&gt;:
    &lt;/p&gt;

    &lt;p&gt;
      I will put another one in the ring:
      &lt;a href=&quot;https://ki-editor.github.io/ki-editor/&quot;&gt;Ki&lt;/a&gt;
    &lt;/p&gt;

    &lt;p&gt;Ki is similar to vim in that it uses modal editing.&lt;/p&gt;

    &lt;p&gt;Its similar to helix in that:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;It selects first, then acts on the selection.&lt;/li&gt;
      &lt;li&gt;It has first-class multi-cursor support&lt;/li&gt;
      &lt;li&gt;It aims to be low-config&lt;/li&gt;
      &lt;li&gt;
        It has built-in LSP support. Adding a new language is a matter of
        declaring it. (I added 2 on my own yesterday, within 2 hours of using
        Ki. I could never have dared do this with vim/neovim).
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;
      Its different from both vim and Helix in that it splits the mental model
      into:
    &lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;selection mode;&lt;/li&gt;
      &lt;li&gt;movement;&lt;/li&gt;
      &lt;li&gt;action&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;such that:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        A selection mode sets the current &lt;em&gt;unit&lt;/em&gt; -
        &lt;i&gt;column/character, word, line, syntax node&lt;/i&gt;, the latter two of
        which are semantic units derived from tree-sitter grammar.
      &lt;/li&gt;
      &lt;li&gt;
        Because selection unit is already configured, movements are reduced to
        &lt;code&gt;hjkl&lt;/code&gt;.
      &lt;/li&gt;
      &lt;li&gt;Actions, like helix, then act on the current selection.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;
      I think I made the explanation more complicated than the actual execution.
    &lt;/p&gt;

    &lt;p&gt;
      I’ve only been exploring it for a day. Navigation through syntax nodes is
      impressive, but also heavily reliant on the language’s tree-sitter grammar
      being decent. Also, I’m not sure how much of a leg up it is against
      helix’s LSP jump to symbol. But helix’s operations on syntax nodes surely
      feels like a second thought, when compared to Ki’s.
    &lt;/p&gt;

    &lt;p&gt;There are other goodies:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        Everything is a buffer. So, same key-bindings are used everywhere.
      &lt;/li&gt;
      &lt;li&gt;
        It has a built-in file-tree explorer (using yaml!), which can be
        fuzzy-searched too.
      &lt;/li&gt;
      &lt;li&gt;
        Thought-out keybindings. For example, choosing between editor and system
        clipboard is a matter of &lt;kbd&gt;\&lt;/kbd&gt; key. &lt;kbd&gt;y&lt;/kbd&gt; or
        &lt;kbd&gt;p&lt;/kbd&gt; copies or pastes to editor clipboard, while
        &lt;kbd&gt;\y&lt;/kbd&gt; or &lt;kbd&gt;\p&lt;/kbd&gt; copies or pastes to the system clipboard.
      &lt;/li&gt;
    &lt;/ul&gt;</content>
      <link href="https://bhoot.dev/2024/ki-first-impressions"/>
      <summary type="html">I tried out a new modal editor called &lt;i&gt;Ki&lt;/i&gt;, and inevitably measured my
    first impressions with Vim and Helix.</summary>
      <category term="Modal editing" label="Modal editing" />
      <published>2024-09-20T12:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:472b7fe6-d06b-428d-91c0-e931e953b421</id>
      <title type="html">How I use &lt;code&gt;:has()&lt;/code&gt; selector to add table of contents</title>
      <updated>2024-09-20T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;
      Copy-pasting my comment in a converstion on lobste.rs titled
      &lt;a href=&quot;https://lobste.rs/s/gery1i/undeniable_utility_css_has#c_unpyso&quot;&gt;The Undeniable Utility Of CSS :has&lt;/a&gt;:
    &lt;/p&gt;

    &lt;p&gt;
      My most recent favourite use of &lt;code&gt;:has&lt;/code&gt; was to target insertion
      of a table of contents (TOC) on pages of my website.
    &lt;/p&gt;

    &lt;p&gt;
      Assuming the following configuration for the static site generator I use
      (&lt;a href=&quot;https://soupault.app&quot;&gt;soupault&lt;/a&gt;):
    &lt;/p&gt;

    &lt;pre&gt;&lt;code&gt;[widgets.table-of-contents]
selector = &quot;hgroup:has(h1)&quot;
action = &quot;insert_after&quot;&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;
      the &lt;code&gt;:has&lt;/code&gt; selector instructs soupault to only insert TOC in
      those pages which have an &lt;code&gt;h1&lt;/code&gt; heading, and to insert it right
      after the first &lt;code&gt;hgroup&lt;/code&gt; of the page, so that the TOC is
      located right after the heading + subtitle + timestamp + tags combo.
    &lt;/p&gt;

    &lt;p&gt;
      &lt;code&gt;:has&lt;/code&gt; is a selector best used judiciously though due to
      performance concerns. Firefox (and presumably other browsers) help by
      displaying a tortoise symbol to flag an unconstrained
      &lt;code&gt;:has&lt;/code&gt; selector.
    &lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/has-selector-for-toc"/>
      <summary type="html">I show off a static site gen soupault, and my solution to add table of
    contents using CSS selectors</summary>
      <category term="CSS" label="CSS" />
<category term="Website Meta" label="Website Meta" />
<category term="Soupault" label="Soupault" />
      <published>2024-09-20T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:b2e08fa4-8283-4044-a5ed-84a1531faf94</id>
      <title type="html">Forgotten lesson remembered after staring 5 minutes at the screen: &lt;code&gt;innerHTML&lt;/code&gt; cannot contain a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag.</title>
      <updated>2024-09-12T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Forgotten lesson remembered after staring 5 minutes at the screen: &lt;code&gt;innerHTML&lt;/code&gt; cannot contain a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/no-script-in-innerhtml"/>
      <summary type="html">Forgotten lesson remembered after staring 5 minutes at the screen: &lt;code&gt;innerHTML&lt;/code&gt; cannot contain a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag.</summary>
      <category term="Note" label="Note" />
<category term="JavaScript" label="JavaScript" />
      <published>2024-09-12T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:214c8527-7318-4c6c-ae37-05765bdc353d</id>
      <title type="html">Cool ways to generate a UUID</title>
      <updated>2024-09-07T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;OK, may be not cool enough for Linux veterans, but cool enough for me!&lt;/p&gt;

  &lt;p&gt;There are two ways, in the order of coolness:&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;
      &lt;p&gt;On Linux only: &lt;code&gt;cat /proc/sys/kernel/random/uuid&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cat /proc/sys/kernel/random/uuid
fa199476-f6f3-4a32-ab89-91d81d9cc319&lt;/code&gt;&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;On Linux and macOS: &lt;code&gt;uuidgen&lt;/code&gt;&lt;/p&gt;
      &lt;pre&gt;&lt;code&gt;$ uuidgen
6180fa98-ddd7-4777-a40d-b548db1f3444&lt;/code&gt;&lt;/pre&gt;

      &lt;p&gt;On macOS, &lt;code&gt;uuidgen&lt;/code&gt; gives a UUID in uppercase. You can pipe it to &lt;code&gt;tr&lt;/code&gt; command to translate it to lowercase.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# On macOS
$ uuidgen
58D8D68C-B64D-4E1E-AC0F-B00AFAC1E895

$ uuidgen | tr &quot;[:upper:]&quot; &quot;[:lower:]&quot;
919ae848-df9c-41fa-b59f-de4b933c927c&lt;/code&gt;&lt;/pre&gt;
    &lt;/li&gt;
  &lt;/ol&gt;</content>
      <link href="https://bhoot.dev/2024/cool-ways-to-get-uuid"/>
      <summary type="html">OK, may be not cool enough for Linux veterans, but cool enough for me!</summary>
      <category term="Linux" label="Linux" />
<category term="MacOS" label="MacOS" />
      <published>2024-09-07T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:06da33f8-2f18-431a-99f2-d2085e8a8702</id>
      <title type="html">Choosing curious over judgmental</title>
      <updated>2024-09-07T10:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;&lt;i&gt;Be curious, not judgmental.&lt;/i&gt; I don&#39;t know who said it, but I heard it in the TV Show &lt;i&gt;Ted Lasso&lt;/i&gt;. And boy, is it cathartic in its own way!&lt;/p&gt;

  &lt;p&gt;Just being non-judgmental hasn&#39;t really worked for me in the past. I needed to replace it with something. Curiosity is a good replacement.&lt;/p&gt;

  &lt;p&gt;As a pleasant side effect, this approach freed me from the pressure of being right or the fear of being wrong. If you are judging something or someone, you better be right about it. No such stress anymore!&lt;/p&gt;

  &lt;aside&gt;Somewhat amusingly, this also got me to configure my &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/enforce-browser-fonts/&quot;&gt;Firefox add-on&lt;/a&gt; to allow website-defined fonts by default. Earlier, I had it configured to force specific fonts.&lt;/aside&gt;</content>
      <link href="https://bhoot.dev/2024/curiosity-over-judgement"/>
      <summary type="html">I don&#39;t know about the cat, but curiosity certainly killed the judgmental!</summary>
      <category term="Philosophy of life" label="Philosophy of life" />
      <published>2024-09-07T10:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:c10c93ba-45e0-4f6f-b556-b0988ef852a3</id>
      <title type="html">Scripting with Scala</title>
      <updated>2024-08-24T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Scala 3.5&#39;s CLI now makes scripting with scala seamless! Just a single-file script setup!&lt;/p&gt;

&lt;p&gt;For example, due to the single-file scripting nature imparted by &lt;i&gt;Scala 3.5 CLI&lt;/i&gt;, I was able to embed a Scala script in a &lt;i&gt;node.js&lt;/i&gt; project to analyse parsing of a geometric Point stored in &lt;i&gt;MySQL 8&lt;/i&gt;. The script is &lt;a href=&quot;https://github.com/jbhoot/poc-buggy-geographic-point-parsing-by-node-mysql2/blob/main/jdbc-analysis/main.scala&quot;&gt;hosted on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With Scala 3.5, plus a built-in watcher, and a large scala/jvm ecosystem, Scala has become a good alternative for scripting.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/scala-3_5-scripting"/>
      <summary type="html">Scala 3.5&#39;s CLI now makes scripting with scala seamless! Just a single-file script setup!</summary>
      <category term="Scala" label="Scala" />
      <published>2024-08-24T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:cb0478f2-de68-414b-b6c3-023ef44c13dc</id>
      <title type="html">Task failed successfully - Linux edition</title>
      <updated>2024-08-20T11:00:00.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;figure&gt;
&lt;img src=&quot;/static/images/task-failed-successfully-linux-edition.jpeg&quot; alt=&quot;Screenshot of a message printed by pinfo command on terminal on Linux that says &#39;crash with: Success&#39;&quot;&gt;
&lt;figcaption&gt;I did something to &lt;i&gt;GNU pinfo&lt;/i&gt;.&lt;/figcaption&gt;
&lt;/figure&gt;</content>
      <link href="https://bhoot.dev/2024/task-failed-successfully-linux-edition"/>
      <summary type="html">&lt;img alt=&quot;Post author&#39;s photo&quot; class=&quot;u-photo&quot; src=&quot;/static/images/profile-pic-closeup-round.png&quot; width=&quot;60&quot;&gt;
  &lt;span&gt;
    &lt;span&gt;Written by &lt;a class=&quot;p-author h-card&quot; href=&quot;https://bhoot.dev/about&quot;&gt;Jayesh Bhoot&lt;/a&gt;&lt;/span&gt;
  &lt;/span&gt;</summary>
      <category term="RHCSA" label="RHCSA" />
      <published>2024-08-20T11:00:00.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:4a3058d8-0c53-4aaa-ad99-daf4254eac48</id>
      <title type="html">British v/s American comedy dramas</title>
      <updated>2024-07-07T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;They always felt different from each other, but I could never quite put a finger on the difference.&lt;/p&gt;

&lt;p&gt;Then it hit me today, while I was watching a British series called &lt;a href=&quot;https://tv.apple.com/in/episode/nikki-and-jason/umc.cmc.2dzdkdvedw0n9h25d49y9lh99?action=playSmartEpisode&quot;&gt;Trying&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In American shows, the script feels quite...scripted. Every other dialogue in a conversation is written to be a punchline or a one-liner.&lt;/p&gt;

&lt;p&gt;British dramas feel more organic – a conversation goes on with (wry, dry) humour sprinkled here and there.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/british-vs-american-comedy-drama"/>
      <summary type="html">They always felt different from each other, but I could never quite put a finger on the difference.</summary>
      <category term="Uncollected" label="Uncollected" />
      <published>2024-07-07T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:9905f662-e652-4747-949b-37c1112e1bad</id>
      <title type="html">Stubborn pop-up on &lt;i&gt;Mutual Fund Utilities (MFU)&lt;/i&gt; website</title>
      <updated>2024-06-23T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#the-problem&quot;&gt;The problem&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#the-solution&quot;&gt;The solution&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;Tip for Mutual Fund Utilities (MFU) users:&lt;/p&gt;

&lt;p&gt;While transacting for a new folio, if you are stuck at &quot;Nominee Verify&quot; pop-up even after verifying the nominee(s), then check that:
&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;Whether there are multiple holders of that CAN&lt;/li&gt;
    &lt;li&gt;Whether *all* of the holders have verified the nominee(s)&lt;/li&gt;
  &lt;/ol&gt;

  &lt;img src=&quot;/static/images/mfu-nominee-verification-bug-fix.png&quot; alt=&quot;Screenshot of Nominee Details section in Account Profile, showing whether some or all of the holders of a selected CAN account have verified the nominee details. All holders must have verified the nominee details in order to be able to transact for a new folio.&quot;&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt;I forgot to take screenshots, but here is a close enough description.&lt;/p&gt;

&lt;p&gt;If none of the holders have verified the nominee details, then MFU&#39;s interface in this section provides a &quot;Verify nominee&quot; button *only* for the primary holder.&lt;/p&gt;

&lt;p&gt;Once a primary holder has verified the nominee details, the interface looks like the verification is already complete. But New Folio transaction will still ask you to Verify Nominee. Very frustrating.&lt;/p&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The solution&lt;/h2&gt;

&lt;p&gt;In order for any holders to verify nominee details, use this link: &lt;a href=&quot;https://mfuonline.com/CNOMV&quot;&gt;https://mfuonline.com/CNOMV&lt;/a&gt;&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/mfu-nominee-verification"/>
      <summary type="html">Tip for Mutual Fund Utilities (MFU) users:</summary>
      <category term="Uncollected" label="Uncollected" />
      <published>2024-06-23T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:b1319873-65a5-49e3-82aa-334e3fb7e353</id>
      <title type="html">Effective Scala, part 8 - OO concepts</title>
      <updated>2024-05-14T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Flip the perspective on tools like interfaces and traits. Don&#39;t see them as a way for objects to share an interface, but as an abstraction over implementations.&lt;/p&gt;

&lt;p&gt;So, not: DatabaseAccess -&amp;gt; (InMemoryDatabase, PhysicalDatabase)&lt;/p&gt;

&lt;p&gt;but:     DatabaseAccess &amp;lt;- (InMemoryDatabase, PhysicalDatabase)&lt;/p&gt;

&lt;p&gt;An interface is more usefully seen as:&lt;/p&gt;

&lt;p&gt;- DatabaseAccess creates an abstraction barrier so we can ignore which database is used underneath&lt;/p&gt;

&lt;p&gt;than as:&lt;/p&gt;

&lt;p&gt;- DatabaseAccess allows various databases to share the interface.&lt;/p&gt;

&lt;p&gt;The former encourages the view/use of interface as an abstraction over implementation.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-8-oop"/>
      <summary type="html">Flip the perspective on tools like interfaces and traits. Don&#39;t see them as a way for objects to share an interface, but as an abstraction over implementations.</summary>
      <category term="Effective Scala series" label="Effective Scala series" />
<category term="OOP" label="OOP" />
      <published>2024-05-14T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:5a99b823-fa04-4e97-b94f-a0e57f0f879a</id>
      <title type="html">Series - Effective Programming in Scala</title>
      <updated>2024-05-08T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;This is a series of articles I wrote while going through the Coursera Course &lt;a href=&quot;https://www.coursera.org/learn/effective-scala&quot;&gt;Effective Programming in Scala&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;/2024/effective-scala-1-elements-of-a-program&quot;&gt;Elements of a program&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/effective-scala-2-domain-modelling&quot;&gt;Domain Modelling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/effective-scala-3-compatibility-roulette&quot;&gt;Compatibility roulette in Scala&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/effective-scala-4-method-vs-function&quot;&gt;Function v/s method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/effective-scala-5-collections&quot;&gt;Collections in Scala&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/effective-scala-6-sbt&quot;&gt;Scala Build Tool (SBT)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/effective-scala-7-project-entry-point&quot;&gt;Entry point of a Scala project&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-series"/>
      <summary type="html">This is a series of articles I wrote while going through the Coursera Course &lt;a href=&quot;https://www.coursera.org/learn/effective-scala&quot;&gt;Effective Programming in Scala&lt;/a&gt;.</summary>
      <category term="Scala" label="Scala" />
<category term="Effective Scala series" label="Effective Scala series" />
      <published>2024-05-08T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:3194d9ee-2397-4b1a-ba7a-125dbe10fb30</id>
      <title type="html">Effective Scala, part 7 - entry point of a project</title>
      <updated>2024-05-07T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;While a scala worksheet are evaluated from top to bottom, a scala project needs an entry point.&lt;/p&gt;

&lt;p&gt;Only two requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It needs to be a top-level &lt;em&gt;method&lt;/em&gt; or an object-level method, but not a class-level method&lt;/li&gt;
&lt;li&gt;It needs to be annotated with &lt;code&gt;@main&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Otherwise&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can be named anything.&lt;/li&gt;
&lt;li&gt;It can be put in an arbitrary source file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A source file in a project cannot contain top-level statements (e.g., &lt;code&gt;println&lt;/code&gt;), only top-level definitions (&lt;code&gt;def&lt;/code&gt;, &lt;code&gt;val&lt;/code&gt;, &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;trait&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt; definitions)
.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-7-project-entry-point"/>
      <summary type="html">While a scala worksheet are evaluated from top to bottom, a scala project needs an entry point.</summary>
      <category term="Effective Scala series" label="Effective Scala series" />
      <published>2024-05-07T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:b84ce392-affe-484b-a5eb-fde46cdd0ad8</id>
      <title type="html">Effective Scala, part 6 - Scala Build Tool (SBT)</title>
      <updated>2024-05-06T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#configurations-in-build-sbt&quot;&gt;Configurations in &lt;code&gt;build.sbt&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#scoping-a-setting&quot;&gt;Scoping a Setting&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;src/project&lt;/code&gt; dir contains configuration about SBT - &lt;code&gt;build.properties&lt;/code&gt; to specify sbt version, &lt;code&gt;plugins.sbt&lt;/code&gt; to specify sbt plugins, and so on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/build.sbt&lt;/code&gt; contains the configuration to build the project itself.&lt;/p&gt;

&lt;p&gt;̄An sbt build (&lt;code&gt;build.sbt&lt;/code&gt;) is composed of - settings and tasks. A setting parametrises the build, and consists of a key value pair. A task may also by parametrised by settings.&lt;/p&gt;

&lt;h2 id=&quot;configurations-in-build-sbt&quot;&gt;Configurations in &lt;code&gt;build.sbt&lt;/code&gt;&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Compile (default configuration where sbt looks first)&lt;/li&gt;
&lt;li&gt;Test&lt;/li&gt;
&lt;li&gt;Zero (no specified config, where fallback values live)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;scoping-a-setting&quot;&gt;Scoping a Setting&lt;/h2&gt;

&lt;p&gt;Different values can be applied to a single setting by scoped its key.&lt;/p&gt;

&lt;p&gt;A key can be scoped along any of the 3 axes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration. e.g., &lt;code&gt;Compile / sourceDirectory&lt;/code&gt;, &lt;code&gt;Test / sourceDirectory&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Task. e.g., &lt;code&gt;includeFilter&lt;/code&gt;, &lt;code&gt;unmanagedSources / includeFilter&lt;/code&gt; where the latter &lt;code&gt;includeFilter&lt;/code&gt; is scoped by &lt;code&gt;unmanagedSources&lt;/code&gt; task.&lt;/li&gt;
&lt;li&gt;Sub-project. e.g., &lt;code&gt;mainProject / scalaVersion&lt;/code&gt;, &lt;code&gt;subProject / scalaVersion&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Axes can be combined too: &lt;code&gt;subProject / Compile / unmanagedSources / includeFilter&lt;/code&gt;. I think the order is: &lt;strong&gt;project / configuration / task / key&lt;/strong&gt;.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-6-sbt"/>
      <summary type="html">&lt;code&gt;src/project&lt;/code&gt; dir contains configuration about SBT - &lt;code&gt;build.properties&lt;/code&gt; to specify sbt version, &lt;code&gt;plugins.sbt&lt;/code&gt; to specify sbt plugins, and so on.</summary>
      <category term="Effective Scala series" label="Effective Scala series" />
      <published>2024-05-06T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:b62ea789-d0e9-4e7f-9b0e-40d17322bef7</id>
      <title type="html">Effective Scala, part 5 - collections</title>
      <updated>2024-04-24T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#infix-construct-operators&quot;&gt;Infix construct operators&lt;/a&gt;&lt;ol class=&quot;toc-2&quot;&gt;&lt;li&gt;&lt;a href=&quot;#immutable-operators&quot;&gt;Immutable operators&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#mutable-operators&quot;&gt;Mutable operators&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#list&quot;&gt;List&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#option&quot;&gt;Option&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;Operations on collections can be classified into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;construct&lt;/strong&gt; ops (&lt;code&gt;::&lt;/code&gt;, &lt;code&gt;+:&lt;/code&gt;, &lt;code&gt;:+&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;++&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;query&lt;/strong&gt; ops (&lt;code&gt;size&lt;/code&gt;, &lt;code&gt;empty&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt;, &lt;code&gt;tail&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;); and&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;transform&lt;/strong&gt; ops (&lt;code&gt;map&lt;/code&gt;, &lt;code&gt;fold&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;infix-construct-operators&quot;&gt;Infix construct operators&lt;/h2&gt;

&lt;p&gt;There are a shitload of them, most of them with low recall value to a beginner.&lt;/p&gt;

&lt;h3 id=&quot;immutable-operators&quot;&gt;Immutable operators&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;::&lt;/code&gt; to prepend to a &lt;em&gt;List&lt;/em&gt;. &lt;code&gt;1 :: List.empty&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+:&lt;/code&gt; to prepend to a &lt;code&gt;Sequence&lt;/code&gt; collection, including &lt;code&gt;List&lt;/code&gt;. &lt;code&gt;1 +: mutable.ArrayBuffer(2, 3)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:+&lt;/code&gt; to append to a &lt;code&gt;Sequence&lt;/code&gt; collection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+&lt;/code&gt; to add a (key, value) tuple to a &lt;em&gt;Map&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;++&lt;/code&gt; constructs a new collection out of two collections. &lt;code&gt;Map((1, &quot;1&quot;), (2, &quot;2&quot;)) ++ Map((4, &quot;4&quot;))&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;mutable-operators&quot;&gt;Mutable operators&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;+=:&lt;/code&gt; to prepend to a &lt;code&gt;Sequence&lt;/code&gt; collection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+=&lt;/code&gt; to append to a &lt;code&gt;Sequence&lt;/code&gt; collection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;++=&lt;/code&gt; concatenates two collections, mutates the first one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;list&quot;&gt;List&lt;/h2&gt;

&lt;p&gt;A list is constructed from right to left:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1 :: 2 :: 3 :: Nil&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;option&quot;&gt;Option&lt;/h2&gt;

&lt;p&gt;Option is actually a collection of zero or one element.&lt;/p&gt;

&lt;p&gt;This explains why Option type has a &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;filter&lt;/code&gt; operations even in OCaml.&lt;/p&gt;

&lt;p&gt;No indexed access (&lt;code&gt;opt(0)&lt;/code&gt;) though, understandably.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-5-collections"/>
      <summary type="html">Operations on collections can be classified into:</summary>
      <category term="Effective Scala series" label="Effective Scala series" />
      <published>2024-04-24T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:af3fbc76-728b-45ac-9c02-886b7aa2db68</id>
      <title type="html">Effective Scala, part 4 - function v/s method</title>
      <updated>2024-04-21T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;A function is a value in Scala, while a method is not. So the former can be passed as a parameter of a function or a method, and can be returned as a value. Method cannot do any of those.&lt;/p&gt;

&lt;p&gt;A function is a value in Scala. All values are objects in Scala. An object can have methods. So a function in Scala can have methods.&lt;/p&gt;

&lt;p&gt;Any function in Scala has a default &lt;code&gt;apply&lt;/code&gt; method. &lt;code&gt;inc(1)&lt;/code&gt; is a shorthand for &lt;code&gt;inc.apply(1)&lt;/code&gt;.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-4-method-vs-function"/>
      <summary type="html">A function is a value in Scala, while a method is not. So the former can be passed as a parameter of a function or a method, and can be returned as a value. Method cannot do any of those.</summary>
      <category term="Effective Scala series" label="Effective Scala series" />
      <published>2024-04-21T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:63a0e01e-eacb-486e-9f72-959cc2ccb742</id>
      <title type="html">Effective Scala, part 3 - compatibility roulette</title>
      <updated>2024-04-19T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#problem&quot;&gt;Problem&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#attempt-1&quot;&gt;Attempt 1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#attempt-2&quot;&gt;Attempt 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#tl-dr&quot;&gt;TL;DR&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;I am going through the course &lt;a href=&quot;https://www.coursera.org/learn/effective-scala&quot;&gt;Effective Programming in Scala&lt;/a&gt; on Coursera, and ran into build import errors in the very first assignment (fireworks).&lt;/p&gt;

&lt;h2 id=&quot;problem&quot;&gt;Problem&lt;/h2&gt;

&lt;p&gt;While importing the fireworks project in VSCode-with-Metals, I encountered an error that started with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[..]bad constant pool index: [..]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Looks like an error right out of a database connection pool library!&lt;/p&gt;

&lt;h2 id=&quot;attempt-1&quot;&gt;Attempt 1&lt;/h2&gt;

&lt;p&gt;One of the suggested solutions on Discord was to ensure that the versions of java, scala, and sbt exposed in the PATH are compatible with each other.&lt;/p&gt;

&lt;p&gt;Check the &lt;a href=&quot;https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html&quot;&gt;compatibility tables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But these were already compatible in my case.&lt;/p&gt;

&lt;h2 id=&quot;attempt-2&quot;&gt;Attempt 2&lt;/h2&gt;

&lt;p&gt;I roughly knew that *.sbt files describe the build of a project. Because the name bore relevance to build process, I also counted in a file I noticed in early debugging: &lt;code&gt;build.properties&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So my first small scala project, and I am staring at the following list of *.sbt: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;zsh | ~/Downloads
$ tree -a -P &quot;*.sbt&quot; -P &quot;*.properties&quot; fireworks
fireworks
├── assignment.sbt
├── build.sbt
├── project
│   ├── build.properties
│   ├── buildSettings.sbt
│   └── plugins.sbt
└── src&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And all of them were staring back at me, daring me to find which one of them is hiding the cause of the error.&lt;/p&gt;

&lt;p&gt;Anyway, enough drama.&lt;/p&gt;

&lt;p&gt;On a hunch, I decided to match the versions of sbt and scala mentioned in these .sbt files with the ones exposed in the PATH.&lt;/p&gt;

&lt;p&gt;The mismatch turned out to be in &lt;code&gt;build.properties&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;zsh | ~/projects/effective-scala/week-1/fireworks
$ sbt -V
sbt version in this project: 1.9.9
sbt script version: 1.9.9

$ cat project/build.properties
sbt.version=1.8.2&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I matched the versions to be &lt;code&gt;1.9.9&lt;/code&gt;, and re-ran the import. It worked.&lt;/p&gt;

&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ensure that the versions of java, scala, and sbt exposed in the PATH are compatible with each other. Use the official &lt;a href=&quot;https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html&quot;&gt;compatibility tables&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Ensure that the versions of sbt and scala exposed in the PATH match with the versions mentioned in the build files (like built.sbt, build.properties). Best make them identical.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I hope this was the correct solution.&lt;/p&gt;

&lt;p&gt;As people who make tools to make people&#39;s lives easier, we sure love to torture ourselves with our own tools.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-3-compatibility-roulette"/>
      <summary type="html">I am going through the course &lt;a href=&quot;https://www.coursera.org/learn/effective-scala&quot;&gt;Effective Programming in Scala&lt;/a&gt; on Coursera, and ran into build import errors in the very first assignment (fireworks).</summary>
      <category term="Effective Scala series" label="Effective Scala series" />
      <published>2024-04-19T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:321f0aca-5212-4b8b-8994-946cac15316f</id>
      <title type="html">Effective Scala, part 2 - domain modelling</title>
      <updated>2024-04-16T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Finding the &lt;em&gt;right&lt;/em&gt; level of abstraction is more important than the best level of abstraction.&lt;/p&gt;

&lt;p&gt;For example, in a problem involving doors and windows and facades, if the domain is constrained to only deal with surface area, then modelling the doors and windows and facade in terms of &lt;strong&gt;Rectangle&lt;/strong&gt; is the right level of abstraction instead of modelling them as Door, Window, and Facade respectively.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-2-domain-modelling"/>
      <summary type="html">Finding the &lt;em&gt;right&lt;/em&gt; level of abstraction is more important than the best level of abstraction.</summary>
      <category term="Effective Scala series" label="Effective Scala series" />
      <published>2024-04-16T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:7baad03d-26bd-44b2-a645-36b00f7707a1</id>
      <title type="html">Effective Scala, part 1 - elements of a program</title>
      <updated>2024-04-15T16:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#value--or-data-in-this-context-&quot;&gt;Value (or Data in this context)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#literal&quot;&gt;Literal&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#-data--type&quot;&gt;(Data) Type&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#expression&quot;&gt;Expression&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#program&quot;&gt;Program&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#another-perspective-on-type&quot;&gt;Another perspective on Type&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;h2 id=&quot;value--or-data-in-this-context-&quot;&gt;Value (or Data in this context)&lt;/h2&gt;

&lt;p&gt;A value is the representation of some entity that can be manipulated by a program.&lt;/p&gt;

&lt;p&gt;Value is the most abstract definition here, but it is made concrete by subsequent definitions.&lt;/p&gt;

&lt;h2 id=&quot;literal&quot;&gt;Literal&lt;/h2&gt;

&lt;p&gt;A literal is a text representation of a value in source code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt; is an integer literal&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;1&quot;&lt;/code&gt; is a string literal&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(x) =&amp;gt; x * x&lt;/code&gt; is a &lt;strong&gt;function literal&lt;/strong&gt; aka anonymous function in JavaScript&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{id: 1, (x) =&amp;gt; x * x}&lt;/code&gt; is an &lt;strong&gt;object literal&lt;/strong&gt; in JavaScript, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;-data--type&quot;&gt;(Data) Type&lt;/h2&gt;

&lt;p&gt;A data type (or simply type) is a collection of data values.&lt;/p&gt;

&lt;p&gt;A type is usually specified by a set of possible values, and a set of allowed &lt;strong&gt;operations&lt;/strong&gt; on these
values.&lt;/p&gt;

&lt;p&gt;The members of a type are the values of that type.&lt;/p&gt;

&lt;h2 id=&quot;expression&quot;&gt;Expression&lt;/h2&gt;

&lt;p&gt;An expression is a syntactic entity that can evaluate to a value.&lt;/p&gt;

&lt;p&gt;An expression combines literals, constants,
variables, and expressions, &lt;strong&gt;by applying operations&lt;/strong&gt; (functions, operators, methods) on them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt; is an integer literal that is also a simple expression that evaluates to value &lt;code&gt;1&lt;/code&gt;. Value
&lt;code&gt;1&lt;/code&gt; belongs to type &lt;code&gt;Integer&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;Alice&quot; + &quot; Wonderland&quot;&lt;/code&gt; is an expression that combines two string literals by applying the operation
&lt;code&gt;+&lt;/code&gt; on
them. Literal &lt;code&gt;&quot;Alice&quot;&lt;/code&gt; represents the value &lt;code&gt;Alice&lt;/code&gt;. The value &lt;code&gt;Alice&lt;/code&gt; belongs to type
&lt;code&gt;String&lt;/code&gt;. The operation &lt;code&gt;+&lt;/code&gt; is an operator/method defined on type &lt;code&gt;String&lt;/code&gt; and can be applied on literals, constants, variables that represent a value of type &lt;code&gt;String&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(&quot;Alice&quot; + &quot; Wonderland&quot;).toUpperCase&lt;/code&gt; is an expression in which operation &lt;code&gt;toUpperCase&lt;/code&gt; is
applied on the expression &lt;code&gt;&quot;Alice&quot; + &quot; Wonderland&quot;&lt;/code&gt;. The expression &lt;code&gt;&quot;Alice&quot; + &quot; Wonderland&quot;&lt;/code&gt; evaluates to a value &lt;code&gt;Alice Wonderland&lt;/code&gt; of type &lt;code&gt;String&lt;/code&gt;. The operation &lt;code&gt;toUpperCase&lt;/code&gt; is a method defined on type &lt;code&gt;String&lt;/code&gt; and can be applied on literals, constants, variables that represent a value of type &lt;code&gt;String&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;program&quot;&gt;Program&lt;/h2&gt;

&lt;p&gt;A program expresses a computation. (A computation is any type of arithmetic or non-arithmetic calculation that is
well-defined).&lt;/p&gt;

&lt;p&gt;A program is a sequence of expressions (and/or statements too, but let&#39;s keep it simple here).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 + 1&lt;/code&gt; is an expression, but also a program which expresses the computation of adding 1 to 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;another-perspective-on-type&quot;&gt;Another perspective on Type&lt;/h2&gt;

&lt;p&gt;Types define the rules for combining expressions. The types define how the expressions can be combined, by applying
operations to them. For this reason, operations are also called members of types.&lt;/p&gt;

&lt;p&gt;For instance, the &amp;amp;&amp;amp; operation (and) is available on the type Boolean, and it expects another Boolean value on its
right-hand side. We say that the type Boolean has a member named &amp;amp;&amp;amp; (and), which takes another Boolean value as a
parameter.&lt;/p&gt;

&lt;p&gt;If you try to apply an operation to an expression,
whose type does not provide such an operation, it&#39;s an error.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/effective-scala-1-elements-of-a-program"/>
      <summary type="html">A value is the representation of some entity that can be manipulated by a program.</summary>
      <category term="Effective Scala series" label="Effective Scala series" />
      <published>2024-04-15T16:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:6ac9fa5c-7838-41ef-ad83-d460315fa18c</id>
      <title type="html">An overview of OCaml editor tooling</title>
      <updated>2024-04-15T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#relevant-background&quot;&gt;Relevant background&lt;/a&gt;&lt;ol class=&quot;toc-2&quot;&gt;&lt;li&gt;&lt;a href=&quot;#package-and-build-tooling&quot;&gt;Package and build tooling&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#editor-service---merlin&quot;&gt;Editor service - &lt;strong&gt;Merlin&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#editor-service---lsp&quot;&gt;Editor service - LSP&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#how-everything-comes-together&quot;&gt;How everything comes together&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#merlin-v-s-ocaml-lsp&quot;&gt;merlin v/s ocaml-lsp&lt;/a&gt;&lt;ol class=&quot;toc-3&quot;&gt;&lt;li&gt;&lt;a href=&quot;#feature-parity&quot;&gt;Feature parity&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#dependency-on-dune&quot;&gt;Dependency on dune&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#speed&quot;&gt;Speed&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#editing-setups&quot;&gt;Editing setups&lt;/a&gt;&lt;ol class=&quot;toc-2&quot;&gt;&lt;li&gt;&lt;a href=&quot;#official--popular-setups&quot;&gt;Official, popular setups&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#unofficial--popular-setups&quot;&gt;Unofficial, popular setups&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#worthy-outliers&quot;&gt;Worthy outliers&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#my-excursions&quot;&gt;My excursions&lt;/a&gt;&lt;ol class=&quot;toc-2&quot;&gt;&lt;li&gt;&lt;a href=&quot;#merlin---emacs&quot;&gt;merlin + emacs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#merlin---vim&quot;&gt;merlin + vim&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#ocaml-lsp---vscode&quot;&gt;ocaml-lsp + vscode&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#ocaml-lsp---neovim&quot;&gt;ocaml-lsp + neovim&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#ocaml-lsp---helix&quot;&gt;ocaml-lsp + helix&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;h2 id=&quot;relevant-background&quot;&gt;Relevant background&lt;/h2&gt;

&lt;h3 id=&quot;package-and-build-tooling&quot;&gt;Package and build tooling&lt;/h3&gt;

&lt;p&gt;Over time, the OCaml community has converged on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;opam&lt;/strong&gt; as a package manager and a project environment handler&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dune&lt;/strong&gt; as a build system&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ocamlformat&lt;/strong&gt; as a code formatter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other tools in these spaces exist, but the above two are officially recommended, and relatively more popular.&lt;/p&gt;

&lt;h3 id=&quot;editor-service---merlin&quot;&gt;Editor service - &lt;strong&gt;Merlin&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Merlin&lt;/strong&gt; is a &quot;headless&quot; tool which provides modern IDE features for OCaml/ReasonML code.&lt;/p&gt;

&lt;p&gt;Supporting OCaml in an editor/IDE is essentially a matter of talking to Merlin.&lt;/p&gt;

&lt;h3 id=&quot;editor-service---lsp&quot;&gt;Editor service - LSP&lt;/h3&gt;

&lt;p&gt;With the advent of VS Code and Language Server Protocol, &lt;strong&gt;ocaml-lsp&lt;/strong&gt; was built as an LSP wrapper on top of Merlin.&lt;/p&gt;

&lt;h3 id=&quot;how-everything-comes-together&quot;&gt;How everything comes together&lt;/h3&gt;

&lt;p&gt;A glue package integrates the editor service (merlin or ocaml-lsp) with an editor.&lt;/p&gt;

&lt;p&gt;The glue package assumes that the editor service is available in the user environment. The recommended approach is to have opam create a project-specific environment (called a &lt;b&gt;opam local switch&lt;/b&gt;), and install merlin or ocaml-lsp in that env.&lt;/p&gt;

&lt;h3 id=&quot;merlin-v-s-ocaml-lsp&quot;&gt;merlin v/s ocaml-lsp&lt;/h3&gt;

&lt;h4 id=&quot;feature-parity&quot;&gt;Feature parity&lt;/h4&gt;

&lt;p&gt;In terms of features, both ocaml-lsp and merlin are pretty much on par with each other.&lt;/p&gt;

&lt;p&gt;ocaml-lsp lacks one feature though: determining the type of a selected block of code. Its a pretty nifty feature thanks to OCaml&#39;s strong typing system and merlin.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://discuss.ocaml.org/t/merlin-vs-ocaml-lsp/8887/2&quot;&gt;Quoting Marek Kubica&lt;/a&gt; from OCaml forums:&lt;/p&gt;

&lt;blockquote cite=&quot;https://discuss.ocaml.org/t/merlin-vs-ocaml-lsp/8887/2&quot;&gt;
The only thing I’ve used with Merlin directly that I don’t have at the moment is determining the type of a selection.
&lt;/blockquote&gt;

&lt;h4 id=&quot;dependency-on-dune&quot;&gt;Dependency on dune&lt;/h4&gt;

&lt;p&gt;ocaml-lsp depends on dune to generate type information. Merlin does not need it. Legacy projects predating dune have found this to be a hurdle in transitioning to ocaml-lsp.&lt;/p&gt;

&lt;h4 id=&quot;speed&quot;&gt;Speed&lt;/h4&gt;

ocaml-lsp, despite being an abstraction on top of merlin, feel faster than the direct merlin because ocaml-lsp operates asynchronously.

&lt;p&gt;&lt;a href=&quot;https://discuss.ocaml.org/t/merlin-vs-ocaml-lsp/8887/4&quot;&gt;Quoting Rudi Grinberg&lt;/a&gt; from OCaml forums:&lt;/p&gt;

&lt;blockquote cite=&quot;https://discuss.ocaml.org/t/merlin-vs-ocaml-lsp/8887/4&quot;&gt;
In my experience the editor frontends that come with merlin perform poorly compared to their LSP counterparts. In Emacs for example, I recall that switching to eglot to ocamllsp gave me a very noticeable boost in completion performance and error checking performance.
&lt;/blockquote&gt;

&lt;p&gt;In a (significantly) large codebase, the editing experience in vim/emacs+merlin has reported to become less responsive and more laggy.&lt;/p&gt;


&lt;h2 id=&quot;editing-setups&quot;&gt;Editing setups&lt;/h2&gt;

&lt;p&gt;I will list a few editing setups I have seen in use.&lt;/p&gt;

&lt;h3 id=&quot;official--popular-setups&quot;&gt;Official, popular setups&lt;/h3&gt;

&lt;p&gt;These are official in the sense that setup instructions are &lt;a href=&quot;https://ocaml.org/docs/set-up-editor&quot;&gt;available on the official website.&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;merlin + emacs&lt;/li&gt;
&lt;li&gt;merlin + vim&lt;/li&gt;
&lt;li&gt;ocaml-lsp + vscode&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;unofficial--popular-setups&quot;&gt;Unofficial, popular setups&lt;/h3&gt;

&lt;p&gt;Thanks to ocaml-lsp, alternatives have emerged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ocaml-lsp + neovim&lt;/li&gt;
&lt;li&gt;ocaml-lsp + emacs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;worthy-outliers&quot;&gt;Worthy outliers&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;IntelliJ with &lt;a href=&quot;https://plugins.jetbrains.com/plugin/9440-reasonml&quot;&gt;ReasonML&lt;/a&gt; plugin by Herve Giraud. The only one that does not use merlin underneath, rather takes advantage of IntelliJ&#39;s IDE infrastructure.&lt;/li&gt;
&lt;li&gt;ocaml-lsp + helix&lt;/li&gt;
&lt;li&gt;Anything else that can work with LSP protocol, like KDE&#39;s Kate editor&lt;/li&gt;
&lt;li&gt;Thanks to a strong static typing with a very fast compiler and a built tool with watch mode, some ditch tooling altogether and go with plain vim or emacs or even &lt;a href=&quot;https://discuss.ocaml.org/t/which-editor-ide-do-you-use-when-developing-ocaml-projects/362/27&quot;&gt;nano&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;my-excursions&quot;&gt;My excursions&lt;/h2&gt;

&lt;p&gt;I care a lot about vim-like modal editing.&lt;/p&gt;

&lt;p&gt;The other thing I care about: less hassle to set up.&lt;/p&gt;

&lt;p&gt;Now, let&#39;s see how some of these have fared for me.&lt;/p&gt;

&lt;h3 id=&quot;merlin---emacs&quot;&gt;merlin + emacs&lt;/h3&gt;

&lt;p&gt;The glue packages in this case are &lt;strong&gt;tuareg&lt;/strong&gt; and &lt;strong&gt;user-setup&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately, &lt;a href=&quot;https://ocaml.org/docs/set-up-editor#vim-and-emacs&quot;&gt;official instructions&lt;/a&gt; didn&#39;t work for me. Instructions &lt;a href=&quot;https://dev.realworldocaml.org/install.html#editor-setup&quot;&gt;from Real World OCaml&lt;/a&gt; did:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ opam install user-setup tuareg ocamlformat merlin
$ opam user-setup install&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There is another alternative to tuareg - &lt;strong&gt;caml-mode&lt;/strong&gt;, but I haven&#39;t tried it.&lt;/p&gt;

&lt;p&gt;Tuareg also provides a &lt;i&gt;Send to REPL for evaluation&lt;/i&gt; action, which provides a quick way to test and design code on-the-fly.&lt;/p&gt;

&lt;p&gt;This setup works (type inference, error detection, etc.), but is very barebones. I realised I had to do a lot more on Emacs side - install and configure packages like flycheck, eldoc, merlin-eldoc, and what not - to modernise the editing experience. I stopped here.&lt;/p&gt;

&lt;h3 id=&quot;merlin---vim&quot;&gt;merlin + vim&lt;/h3&gt;

&lt;p&gt;The glue package is &lt;strong&gt;user-setup&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, this setup did not work due to the following error:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&quot;src/soup.ml&quot; 1326L, 39757B
Error detected while processing BufRead Autocommands for &quot;*.ml&quot;..FileType Autocommands for &quot;*&quot;..function &amp;lt;SNR&amp;gt;4_LoadFTPlugin[18]..script /Users/jb/projects/lambdasoup/_opam
/share/merlin/vim/ftplugin/ocaml.vim[2]../Users/jb/projects/lambdasoup/_opam/share/merlin/vim/autoload/merlin.vim:
line    9:
Error: Required vim compiled with +python or +python3
Error detected while processing BufRead Autocommands for &quot;*.ml&quot;..FileType Autocommands for &quot;*&quot;..function &amp;lt;SNR&amp;gt;4_LoadFTPlugin[18]..script /Users/jb/projects/lambdasoup/_opam
/share/merlin/vim/ftplugin/ocaml.vim:
line    2:
E117: Unknown function: merlin#Register
Press ENTER or type command to continue&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I get the issue of course: my vim is not compiled with python. But it also means that now I have to look for a vim that is compiled with python. And I tried out two vims: macOS&#39;s default vim, and the vim in nixpkgs.&lt;/p&gt;

&lt;p&gt;So I moved on.&lt;/p&gt;

&lt;h3 id=&quot;ocaml-lsp---vscode&quot;&gt;ocaml-lsp + vscode&lt;/h3&gt;

&lt;p&gt;The glue package is &lt;strong&gt;VSCode OCaml Platform&lt;/strong&gt;, which, besides integrating ocaml-lsp, also brings together ocamlformat, opam environment sandbox selection tool, etc., under a single VS Code extension.&lt;/p&gt;

&lt;p&gt;Despite that fact that there is one more abstraction to deal with - ocaml-lsp built on top of merlin - this setup has been one of the most seamless.&lt;/p&gt;

&lt;p&gt;It is also the top official recommendation now, thanks to a lot of effort put in by OCaml tooling developers.&lt;/p&gt;

&lt;p&gt;The extension also provides a &quot;Send to REPL for evaluation&quot; action, much popular with the Emacs crowd.&lt;/p&gt;

&lt;p&gt;The experience hasn&#39;t been 100% flawless though. Once, when I opened VS Code with an OCaml project, the editor didn&#39;t show any type hints at all. No errors were thrown either. After a long debugging session, it turned out that the OCaml LSP did throw an error saying that VS Code needs to be updated, but it was buried in Output Pane&#39;s OCaml LSP tab. I wish that the Output window had popped up on its own to highlight the error, instead of having to look for the problem myself. I have seen this happen in some other language project.&lt;/p&gt;

&lt;p&gt;This is VS Code specific, but I also miss a good modal editing experience. I also keep getting bugged by a weird problem caused due to my MacBook&#39;s virtual Fn bar and while executing a specific keychord sequence, in which a slight touch from one of my raised fingers triggers the find window in VS Code. Very annoying!&lt;/p&gt;

&lt;h3 id=&quot;ocaml-lsp---neovim&quot;&gt;ocaml-lsp + neovim&lt;/h3&gt;

&lt;p&gt;The glue package is &lt;strong&gt;nvim-lspconfig&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This setup has often broken down, though the &lt;strong&gt;fault lied with the constant churn in neovim and neovim&#39;s LSP ecosystem&lt;/strong&gt; more than with OCaml tooling.&lt;/p&gt;

&lt;p&gt;There are a lot of other helper neovim packages to reckon with, like, the configurations of which I copy-paste and then pray for them to keep working.&lt;/p&gt;

&lt;p&gt;When everything works, this is the most elegant setup for me. However, once in a while, when I load an OCaml project in neovim, some setup-related error would pop up.&lt;/p&gt;

&lt;p&gt;Debugging a broken neovim configuration is a nightmare I wouldn&#39;t wish upon my enemy.&lt;/p&gt;

&lt;h3 id=&quot;ocaml-lsp---helix&quot;&gt;ocaml-lsp + helix&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;There is no glue package.&lt;/strong&gt; This has been &lt;strong&gt;the only zero-config setup&lt;/strong&gt; for me.&lt;/p&gt;

&lt;p&gt;Only Helix has worked immediately and flawlessly for me.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;At this point, I would consider the merlin-based setups appropriate only for &lt;strong&gt;veterans and advanced users&lt;/strong&gt;. They are worth it only if you care about having every fringe feature available under merlin and don&#39;t mind going through some pain of setting things up.&lt;/p&gt;

&lt;p&gt;merlin-based setup might also be the only sane option if a project does not use dune as its build tool. But don&#39;t quote me on this.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;beginner&lt;/strong&gt; should absolutely choose VS Code OCaml Platform. When there is a whole new ecosystem to reckon with, you don&#39;t want to be scared away by the editing experience.&lt;/p&gt;

&lt;p&gt;Only Helix provides a similar seamless experience. So VS Code dodgers can choose helix (at the cost of the baggage of a non-vim modal editing).&lt;/p&gt;

&lt;p&gt;To vim lovers, I recommend the ocaml-lsp + neovim route.&lt;/p&gt;

&lt;p&gt;To emacs lovers, I recommend the ocaml-lsp + emacs route.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/editor-tooling-in-ocaml"/>
      <summary type="html">For a small community, OCaml ecosystem is quite rich, maybe too rich, in editor tooling. I have gone through some options over time. Here is an overview for my future self.</summary>
      <category term="OCaml" label="OCaml" />
      <published>2024-04-15T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:aa8b98ca-36f9-4a95-9b18-2b8a75335e17</id>
      <title type="html">Memorising branch order in git rebase</title>
      <updated>2024-03-05T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;In a &lt;code&gt;git rebase x&lt;/code&gt; command, is &lt;code&gt;x&lt;/code&gt; being rebased on the current branch, or vice versa?&lt;/p&gt;

&lt;p&gt;I mentally always read &lt;i&gt;rebase&lt;/i&gt; as &lt;i&gt;rebase on top of&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;So, &lt;code&gt;git rebase master&lt;/code&gt; reads as &lt;strong&gt;&lt;i&gt;git, rebase on top of master&lt;/i&gt;&lt;/strong&gt;, i.e., rebase the current branch on top of master.&lt;/p&gt;

&lt;p&gt;But what about the alternate version of the command: &lt;code&gt;git rebase master feat/x&lt;/code&gt; ?&lt;/p&gt;

&lt;p&gt;Well, git always rebases the &lt;strong&gt;current&lt;/strong&gt; branch.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;git rebase master feat/x&lt;/code&gt; is actually &lt;code&gt;git checkout feat/x &amp;amp;&amp;amp; git rebase master&lt;/code&gt; . So the reading &lt;i&gt;git rebase on top of master&lt;/i&gt; still applies.&lt;/p&gt;

&lt;p&gt;A bit clunky, but good enough for me.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/memorising-git-rebase"/>
      <summary type="html">In a &lt;code&gt;git rebase x&lt;/code&gt; command, is &lt;code&gt;x&lt;/code&gt; being rebased on the current branch, or vice versa?</summary>
      <category term="Git" label="Git" />
      <published>2024-03-05T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:972047d6-1709-42ab-b18e-fa68f6a760b5</id>
      <title type="html">&lt;del&gt;I switched to fish shell&lt;/del&gt; Nope. Back to bash.</title>
      <updated>2024-02-06T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#update--5-may-2024&quot;&gt;Update: 5 May 2024&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#original-article&quot;&gt;Original article&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;h2 id=&quot;update--5-may-2024&quot;&gt;Update: 5 May 2024&lt;/h2&gt;

&lt;p&gt;I have come crawling back to bash. Why?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My interest in system, infrastructure, and developer tooling is growing. I don&#39;t see much place for a non-bash shell in that space. &lt;/li&gt;
&lt;li&gt;I have time for only one niche ecosystem in my life. I have already chosen OCaml as it.&lt;/li&gt;
&lt;li&gt;fish is too syntax-incompatible with bash, right down to inline declaration of env vars before executing a command. If I have to recall the fish syntax for every little thing, then it&#39;s just not worth it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;original-article&quot;&gt;Original article&lt;/h2&gt;

&lt;p&gt;Here, I am putting down my reasons to choose &lt;code&gt;fish&lt;/code&gt; shell for my future self.&lt;/p&gt;

&lt;p&gt;All I want is a shell that is&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a first-class citizen on macOS and Linux&lt;/li&gt;
&lt;li&gt;low maintenance, which means&lt;/li&gt;
&lt;li&gt;out-of-the-box or low-configuration autocompletion and fuzzy history search&lt;/li&gt;
&lt;li&gt;adequately supported by third-party tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don&#39;t use shell much beyond that.&lt;/p&gt;

&lt;p&gt;I also don&#39;t care much about scripting compabilities as I have never had to share scripts with others. Also, I prefer to write my scripts in typed, FP languages like Scala.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fish&lt;/code&gt; checks all those boxes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bash&lt;/code&gt; is anything but first-class on macOS now.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;zsh&lt;/code&gt; is powerful, and comes with a lot of bells and whistles, most of them do not come out-of-the-box. I need to lug around a few packages (like &lt;code&gt;zsh-completions&lt;/code&gt;) and a lot of configuration. I also need to maintain configuration for &lt;code&gt;fzf&lt;/code&gt; for fuzzy search.&lt;/p&gt;

&lt;p&gt;At the very least, I will stop worrying and wasting time in maintaining parity between bash and zsh configs.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2024/switching-to-fish-shell"/>
      <summary type="html">I have come crawling back to bash. Why?</summary>
      <category term="Shell" label="Shell" />
      <published>2024-02-06T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:e5759f6b-3be5-4219-aad2-35e9d8c1ebe2</id>
      <title type="html">Make an opam local switch and &lt;code&gt;dune init project&lt;/code&gt; work together</title>
      <updated>2023-12-03T11:31:22.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#local-switch-in-opam&quot;&gt;Local switch in opam&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#dune-init-project&quot;&gt;dune init project&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#the-problem&quot;&gt;The problem&lt;/a&gt;&lt;ol class=&quot;toc-2&quot;&gt;&lt;li&gt;&lt;a href=&quot;#attempt-1&quot;&gt;Attempt 1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#attempt-2&quot;&gt;Attempt 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#solution&quot;&gt;Solution&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#what-next-&quot;&gt;What next?&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;In the OCaml ecosystem, opam is the most popular package management tool, while dune is the foremost build tool.&lt;/p&gt;

&lt;p&gt;Both have evolved independently. While they manage to work in tandem, there are a lot of irregularities to trip on
where they meet.&lt;/p&gt;

&lt;h2 id=&quot;local-switch-in-opam&quot;&gt;Local switch in opam&lt;/h2&gt;

&lt;p&gt;opam&#39;s local switch feature can set up and isolate development environment and dependencies per project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;opam switch create&lt;/code&gt; creates a local switch if it is given the path to a directory. If the specified
directory does not exist, then its is created.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;opam switch create ./path/to/existing/dir&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above command creates an &lt;i&gt;_opam&lt;/i&gt; directory inside the given path, inside which all of the opam-related
artifacts for this project are stored.&lt;/p&gt;

&lt;p&gt;A cool side-effect is that when a directory containing a local opam switch is visited, that switch is activated
automatically. No need to &lt;code&gt;opam switch set&lt;/code&gt; the switch explicitly.&lt;/p&gt;

&lt;h2 id=&quot;dune-init-project&quot;&gt;dune init project&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;dune init project&lt;/code&gt; is the recommended way to bootstrap an OCaml project.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;dune init project prj_name&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt; The above command creates a directory named &lt;i&gt;prj_name&lt;/i&gt; in the current directory, and populates it with project
artifacts.&lt;/p&gt;

&lt;p&gt;I found that dune cannot bootstrap a project in the current directory itself. So &lt;code&gt;dune init project .&lt;/code&gt;
fails to work.&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;

&lt;p&gt; I prefer to use opam&#39;s local switch feature to ensure the isolatation of all dependencies and tools related to an
OCaml project. This means I also prefer to install dune per-project. So &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I want to use dune to bootstrap a project&lt;/li&gt;
&lt;li&gt;dune is installed through opam&lt;/li&gt;
&lt;li&gt;opam needs a local switch for per-project installation&lt;/li&gt;
&lt;li&gt;A local switch needs an existing empty project directory&lt;/li&gt;
&lt;li&gt;dune cannot bootstrap a project in a current directory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The chicken and egg problem looks easy now, huh?&lt;/p&gt;

&lt;p&gt;I started guessing a solution. &lt;/p&gt;

&lt;hgroup&gt;
&lt;h3 id=&quot;attempt-1&quot;&gt;Attempt 1&lt;/h3&gt;
&lt;p&gt;Execute opam and dune commands from parent directory&lt;/p&gt;
&lt;/hgroup&gt;

&lt;p&gt;I planned the following idealised sequence of commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# create an empty project dir
~/projects $ mkdir my_prj

# create a local switch
~/projects $ opam switch create ./my_prj

# install dune in our local switch
~/projects $ opam install dune

# bootstrap project
~/projects $ dune init project my_prj&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I didn&#39;t make it past the 3rd command of installing dune because of the following (reasonable) error:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ opam install dune
[ERROR] No switch is currently set. Please use &#39;opam switch&#39;
to set or install a switch.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ok, then. I tried to set the switch after creating it.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;~/projects $ mkdir my_prj
~/projects $ opam switch create ./my_prj
~/projects $ opam switch set ./my_prj
[ERROR] Can not set external switch &#39;/Users/jb/projects/my_prj&#39; globally. To set
it in the current shell use:
eval $(opam env --switch=/Users/jb/projects/my_prj --set-switch)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The advice felt like a long-winded solution to me. I wanted a simpler solution.&lt;/p&gt;

&lt;p&gt;So may be, first, I should enter the empty project directory to set up the project.&lt;/p&gt;

&lt;hgroup&gt;
&lt;h3 id=&quot;attempt-2&quot;&gt;Attempt 2&lt;/h3&gt;
&lt;p&gt;Execute opam and dune commands from project directory&lt;/p&gt;
&lt;/hgroup&gt;

&lt;p&gt;I carried out the following sequence of commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;~/projects $ mkdir my_prj
~/projects $ cd my_prj 
~/projects/my_prj $ opam switch create . 
~/projects/my_prj $ opam install dune
~/projects/my_prj $ dune init project my_prj&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above sequence creates a &lt;code&gt;my_prj/my_prj/&lt;/code&gt; directory tree. That&#39;s not what I wanted.&lt;/p&gt;

&lt;p&gt;Now what?&lt;/p&gt;

&lt;hgroup&gt;
&lt;h3 id=&quot;solution&quot;&gt;Solution&lt;/h3&gt;
&lt;p&gt;exploit opam&#39;s auto-activation of local switch&lt;/p&gt;
&lt;/hgroup&gt;

&lt;p&gt;Now, consider the following sequence of commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# create an empty project dir
~/projects $ mkdir my_prj

~/projects $ cd my_prj 

# create a local switch, which is auto-activated after a successful creation
# because the switch is created in the current directory itself.
~/projects/my_prj $ opam switch create . 

~/projects/my_prj $ opam install dune

# go back to parent, with opam still pointing to the local switch env
# until the current session ends or until another switch is activated
~/projects $ cd .. 

# bootstrap our project
~/projects/my_prj $ dune init project my_prj&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note the 3rd command &lt;code&gt;opam switch create .&lt;/code&gt;. After the command finishes execution, the switch is
auto-activated because the local switch lives in the current directory &lt;code&gt;.&lt;/code&gt; itself.&lt;/p&gt;

&lt;p&gt;opam also sets an env var &lt;code&gt;OPAM_SWITCH_PREFIX&lt;/code&gt; to the path of this activated switch.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ echo $OPAM_SWITCH_PREFIX 
/Users/jb/projects/my_prj/_opam&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This env var probably lives on until the current shell session ends even if the directory changes, or until another
switch is activated.&lt;/p&gt;

&lt;aside&gt;
&lt;p&gt;If the directory is changed at this point, then &lt;code&gt;opam switch list&lt;/code&gt; no longer shows an active switch.
However, &lt;code&gt;OPAM_SWITCH_PREFIX&lt;/code&gt; still points to our my_prj local switch until a directory with another local
switch is visited.&lt;/p&gt;

&lt;p&gt;Also, my machine&#39;s opam setup does not a global switch at all. I am not sure how opam would behave amidst all of this
in presence of a global switch. A case for another time, may be.&lt;/p&gt;
&lt;/aside&gt;

&lt;p&gt;That means that when the above command sequence goes one directory up to execute &lt;code&gt;dune init project&lt;/code&gt;, opam
is still pointing to the right local switch env.&lt;/p&gt;

&lt;p&gt;Also dune does not mind if the directory &lt;i&gt;my_prj&lt;/i&gt; already exists.&lt;/p&gt;

&lt;p&gt;Case solved!&lt;/p&gt;

&lt;p&gt;I can imagine a variation of the above sequence:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# create an empty project dir
~/projects $ mkdir my_prj

# create our local switch
~/projects $ opam switch create ./my_prj 

# activate our local switch
~/projects $ cd my_prj 

# go back to parent, with opam still pointing to the local switch env
# until the current session ends or until another switch is activated
~/projects/my_prj $ cd .. 

# install dune in our local switch
~/projects $ opam install dune

# bootstrap our project
~/projects $ dune init project my_prj&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;what-next-&quot;&gt;What next?&lt;/h2&gt;

&lt;p&gt;There is &lt;a href=&quot;https://discuss.ocaml.org/t/extending-dune-with-package-management/5649&quot;&gt;work under progress&lt;/a&gt; to
wrap dune around opam, thus making dune as the unifying package management and build solution.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2023/opam-local-switch-and-dune-init-project"/>
      <summary type="html">In the OCaml ecosystem, opam is the most popular package management tool, while dune is the foremost build tool.</summary>
      <category term="OCaml" label="OCaml" />
      <published>2023-12-03T11:31:22.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:6e7ebece-1f47-4461-b284-a0094aacf55e</id>
      <title type="html">Spoonfeeding machine architecture to OCaml toolchain on Apple Silicon</title>
      <updated>2023-12-01T11:31:22.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#problem&quot;&gt;Problem&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#hacky-solution&quot;&gt;Hacky solution&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#update--23-feb-2024----better-solution&quot;&gt;Update (&lt;time datetime=&quot;2024-02-23&quot;&gt;23 Feb 2024&lt;/time&gt;) : Better solution&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;h2 id=&quot;problem&quot;&gt;Problem&lt;/h2&gt;

&lt;p&gt;OCaml compiler failed to build on my M1 Macbook with Apple&#39;s Silicon architecture.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ opam switch create . 4.14.1 -y --deps-only

&amp;lt;&amp;gt;&amp;lt;&amp;gt; Installing new switch packages &amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;  🐫 
Switch invariant: [&quot;ocaml-base-compiler&quot; {= &quot;4.14.1&quot;} | &quot;ocaml-system&quot; {= &quot;4.14.1&quot;}]
[NOTE] External dependency handling not supported for OS family &#39;macos&#39;.
        You can disable this check using &#39;opam option --global depext=false&#39;

&amp;lt;&amp;gt;&amp;lt;&amp;gt; Processing actions &amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;  🐫 
∗ installed base-bigarray.base
∗ installed base-threads.base
∗ installed base-unix.base
∗ installed ocaml-options-vanilla.1
⬇ retrieved ocaml-base-compiler.4.14.1  (cached)
[ERROR] The compilation of ocaml-base-compiler.4.14.1 failed at
        &quot;make -j7&quot;.

#=== ERROR while compiling ocaml-base-compiler.4.14.1 ===========#
# context     2.1.5 | macos/arm64 |  | https://opam.ocaml.org#bc52affc
# path        ~/projects/aoc-2023/ocaml/_opam/.opam-switch/build/ocaml-base-compiler.4.14.1
# command     ~/.opam/opam-init/hooks/sandbox.sh build make -j7
# exit-code   2
# env-file    ~/.opam/log/ocaml-base-compiler-11985-7b627b.env
# output-file ~/.opam/log/ocaml-base-compiler-11985-7b627b.out
### output ###
# [...]
# signals_nat.c:221:5: error: no member named &#39;__pc&#39; in &#39;struct __darwin_x86_thread_state64&#39;
#     CONTEXT_PC = (context_reg) &amp;amp;caml_stack_overflow;
#     ^~~~~~~~~~
# ./signals_osdep.h:182:37: note: expanded from macro &#39;CONTEXT_PC&#39;
#   #define CONTEXT_PC (CONTEXT_STATE.__pc)
#                       ~~~~~~~~~~~~~ ^
# 4 errors generated.
# make[3]: *** [signals_nat.n.o] Error 1
# make[3]: *** Waiting for unfinished jobs....
# make[2]: *** [makeruntimeopt] Error 2
# make[1]: *** [opt.opt] Error 2
# make: *** [world.opt] Error 2



&amp;lt;&amp;gt;&amp;lt;&amp;gt; Error report &amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;  🐫 
┌─ The following actions failed
│ λ build ocaml-base-compiler 4.14.1
└─ 
┌─ The following changes have been performed (the rest was aborted)
│ ∗ install base-bigarray         base
│ ∗ install base-threads          base
│ ∗ install base-unix             base
│ ∗ install ocaml-options-vanilla 1
└─ 

&amp;lt;&amp;gt;&amp;lt;&amp;gt; ocaml-base-compiler.4.14.1 troubleshooting &amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;  🐫 
=&amp;gt; A failure in the middle of the build may be caused by build
    parallelism
        (enabled by default).
        Please file a bug report at
    https://github.com/ocaml/opam-repository/issues
=&amp;gt; You can try installing again including --jobs=1
        to force a sequential build instead.
Switch initialisation failed: clean up? (&#39;n&#39; will leave the
switch partially installed) [Y/n] y&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;hacky-solution&quot;&gt;Hacky solution&lt;/h2&gt;

&lt;p&gt;If you have the same issue, then try specifying the architecture explicitly. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ arch -arm64 opam switch create . 4.14.1 -y --deps-only&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Apply the same approach to install packages too.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ arch -arm64 opam install dune&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There has to be a better way though.&lt;/p&gt;

&lt;p&gt;P.S.: Even dune does not work without it!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ arch -arm64 dune exec bin/main.exe&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;update--23-feb-2024----better-solution&quot;&gt;Update (&lt;time datetime=&quot;2024-02-23&quot;&gt;23 Feb 2024&lt;/time&gt;) : Better solution&lt;/h2&gt;

&lt;p&gt;JM from OCaml discord helped me to discover the underlying issue - my Terminal.app was running in Rosetta mode. I had
configured it to run on Rosetta during the initial Silicon-Rosetta churn, but forgotten all about it.&lt;/p&gt;

&lt;p&gt;So, opam, which uses &lt;a href=&quot;https://github.com/ocaml/opam/issues/5853&quot;&gt;&lt;code&gt;uname -a&lt;/code&gt;&lt;/a&gt; to find the
underlying architecture, always got &lt;code&gt;x86_64&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Once I unchecked the &lt;i&gt;Open using Rosetta&lt;/i&gt; option, &lt;code&gt;arch -arm64&lt;/code&gt; was no longer needed.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://apple.stackexchange.com/a/453954&quot;&gt;From what I understand&lt;/a&gt;, if &lt;i&gt;any&lt;/i&gt; tool or binary across a
toolchain is configured to run through Rosetta - be it the Terminal.app, or the shell, or a helper binary like &lt;code&gt;uname&lt;/code&gt;
- then the program using this toolchain could end up using the wrong architecture.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2023/fixing-ocaml-compiler-build-on-apple-silicon"/>
      <summary type="html">OCaml compiler failed to build on my M1 Macbook with Apple&#39;s Silicon architecture.</summary>
      <category term="OCaml" label="OCaml" />
      <published>2023-12-01T11:31:22.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:bea230bc-0afb-47ac-b73e-631dc8691602</id>
      <title type="html">List of movies better than their book counterparts</title>
      <updated>2023-10-09T11:31:22.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt; This list will likely (hopefully) remain short. &lt;/p&gt;

&lt;ol reversed=&quot;reversed&quot;&gt;
&lt;li&gt; Dune series. I felt that words in &lt;i&gt;Dune&lt;/i&gt; (Book 1 of the series) didn&#39;t convey its strongest moments well. &lt;i&gt;Presently&lt;/i&gt;, I am struggling to get into &lt;i&gt;Dune Messiah&lt;/i&gt; (Book 2 of the series). &lt;/li&gt;
&lt;li&gt; Bullet train (at least when compared to the translated-to-English version of the book)&lt;/li&gt;
&lt;/ol&gt;</content>
      <link href="https://bhoot.dev/2023/movie-is-better-than-book"/>
      <summary type="html">This list will likely (hopefully) remain short.</summary>
      <category term="Books" label="Books" />
      <published>2023-10-09T11:31:22.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:de454181-6576-46c5-9017-afffb721ff58</id>
      <title type="html">An elegant triplet of fonts</title>
      <updated>2023-06-12T11:31:22.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt; I use the following set of fonts. &lt;/p&gt;

&lt;table&gt;
&lt;caption&gt;An elegant triplet of fonts that go together&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Font&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sans Serif&lt;/td&gt;
&lt;td&gt;PT Sans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serif&lt;/td&gt;
&lt;td&gt;Gentium Book Plus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monospace&lt;/td&gt;
&lt;td&gt;
&lt;ul&gt;
&lt;li&gt;PT Mono; or&lt;/li&gt;
&lt;li&gt;Latin Modern Mono; or&lt;/li&gt;
&lt;li&gt;Courier; or&lt;/li&gt;
&lt;li&gt;Courier Screenplay; or&lt;/li&gt;
&lt;li&gt;Courier Prime; or&lt;/li&gt;
&lt;li&gt;Monaco&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;h2&gt;Sans-serif I v/s l&lt;/h2&gt;

&lt;p&gt;Many sans serif fonts fail to distinguish between uppercase I and lowercase l. Twitter&#39;s new font is an example of
this.&lt;/p&gt;

&lt;p&gt;On top of that, Twitter started dishing out blue ticks on subscriptions.&lt;/p&gt;

&lt;p&gt;These two features combine to form deceptive Twitter usernames, as demonstrated in &lt;a href=&quot;https://twitter.com/aardrian/status/1591045578544353280&quot;&gt;this tweet by Adrian Roselli&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some famous fonts like Helvetica and Roboto fail at it.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/static/images/appletvplus-font-test-helvetica-fail.png&quot;&gt;
&lt;figcaption&gt;Helvetica has identical looking uppercase I and lowercase l.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
&lt;img src=&quot;/static/images/appletvplus-font-test-roboto-fail.png&quot;&gt;
&lt;figcaption&gt;Roboto has identical looking uppercase I and lowercase l.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;But PT Sans passes this AppleTVPlus test.&lt;/p&gt;

&lt;figure&gt;
&lt;img src=&quot;/static/images/appletvplus-font-test-pt-sans-pass.png&quot;&gt;
&lt;figcaption&gt;PT Sans distinguishes between uppercase I and lowercase l by putting a tail on l.&lt;/figcaption&gt;
&lt;/figure&gt;</content>
      <link href="https://bhoot.dev/2023/an-elegant-triplet-of-fonts"/>
      <summary type="html">I use the following set of fonts.</summary>
      <category term="Fonts" label="Fonts" />
      <published>2023-06-12T11:31:22.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:83afeb98-3ab6-4a2c-a1ea-4601afc53dfb</id>
      <title type="html">How to mark up a code snippet in HTML</title>
      <updated>2022-10-06T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Suppose, we want to mark up the following multi-line snippet of JavaScript code:&lt;/p&gt;

&lt;figure&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function hello(name) {
  return &quot;Hello, &quot; + name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;
A JavaScript function that greets a user Joe with &quot;Hello, Joe!&quot;
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Out of habit, let&#39;s start by wrapping it in a &lt;code&gt;pre&lt;/code&gt; tag.&lt;/p&gt;

&lt;figure&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;pre&amp;gt;
  function hello(name) {
    return &quot;Hello, &quot; + name;
  }
&amp;lt;/pre&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;
Mark up a code snippet with &lt;code&gt;pre&lt;/code&gt; tag only. Looks good enough, but not conveyed good enough.
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;However, the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element&quot;&gt;HTML spec&lt;/a&gt;
defines &lt;code&gt;pre&lt;/code&gt; as&lt;/p&gt;

&lt;blockquote cite=&quot;https://html.spec.whatwg.org/multipage/grouping-content.html#the-pre-element&quot;&gt;
The &lt;code&gt;pre&lt;/code&gt; tag represents a block of &lt;em&gt;preformatted text&lt;/em&gt;, in which structure is represented by
typographic conventions
rather than by elements.
&lt;/blockquote&gt;

&lt;p&gt;So, a &lt;code&gt;pre&lt;/code&gt; tag can contain anything which conveys its semantic meaning at least partly through how its
text is formatted,
like a code snippet, a poem, or an ascii art.&lt;/p&gt;

&lt;p&gt;Clearly, &lt;code&gt;pre&lt;/code&gt; tag is not enough to represent a code snippet. How do we distinguish a code snippet from
other
pre-formatted content? By marking up the snippet with &lt;code&gt;code&lt;/code&gt;, and then wrapping it in a
&lt;code&gt;pre&lt;/code&gt; tag.
&lt;/p&gt;

&lt;figure&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;pre&amp;gt;
  &amp;lt;code&amp;gt;
    function hello(name) {
      return &quot;Hello, &quot; + name;
    }
  &amp;lt;/code&amp;gt;
&amp;lt;/pre&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;
Mark up a multi-line code snippet with &lt;code&gt;code&lt;/code&gt; tag, and then wrap that &lt;code&gt;code&lt;/code&gt; tag
in a &lt;code&gt;pre&lt;/code&gt; tag
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Now, how do we convey that the language of the code snippet is JavaScript?&lt;/p&gt;

&lt;p&gt;HTML spec has an answer to that too:&lt;/p&gt;

&lt;blockquote cite=&quot;https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-code-element&quot;&gt;
There is no formal way to indicate the language of computer code being marked up. Authors who wish to mark code
elements
with the language used, e.g. so that syntax highlighting scripts can use the right rules, can use the class
attribute,
e.g. by adding a class prefixed with &quot;language-&quot; to the element.
&lt;/blockquote&gt;

&lt;p&gt;In short, assign to the &lt;code&gt;code&lt;/code&gt; tag a class named &lt;b&gt;language-name of the programming language&lt;/b&gt;.&lt;/p&gt;

&lt;figure&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;pre&amp;gt;
  &amp;lt;code class=&quot;language-javascript&quot;&amp;gt;
    function hello(name) {
      return &quot;Hello, &quot; + name;
    }
  &amp;lt;/code&amp;gt;
&amp;lt;/pre&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;A &lt;code&gt;code&lt;/code&gt; tag containing a JavaScript snippet should be given a class named
&lt;strong&gt;language-javascript&lt;/strong&gt;.
&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;There is still one improvement left, which applies to &lt;code&gt;pre&lt;/code&gt; tag in general.&lt;/p&gt;

&lt;p&gt;Conveying formatting of a text can be difficult to a visually-challenged user, just like conveying an image is.&lt;/p&gt;

&lt;p&gt;In order to assist such a user with an alternate description, wrap such semi-accessible content in a
&lt;code&gt;figure&lt;/code&gt; tag, and put
the alternate description in a &lt;code&gt;figcaption&lt;/code&gt; tag.
&lt;/p&gt;

&lt;figure&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;figure&amp;gt;
  &amp;lt;pre&amp;gt;
    &amp;lt;code class=&quot;language-javascript&quot;&amp;gt;
      function hello(name) {
        return &quot;Hello, &quot; + name;
      }
    &amp;lt;/code&amp;gt;
  &amp;lt;/pre&amp;gt;
  &amp;lt;figcaption&amp;gt;
      A code tag containing a JavaScript snippet
      should be given a class named language-javascript.
  &amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figcaption&gt;Wrap a &lt;code&gt;pre&lt;/code&gt; tag in a &lt;code&gt;figure&lt;/code&gt; tag, and put a &lt;code&gt;figcaption&lt;/code&gt; tag inside
the &lt;code&gt;figure&lt;/code&gt; tag that provides an
alternate description to the contents of the &lt;code&gt;pre&lt;/code&gt; tag.
&lt;/figcaption&gt;
&lt;/figure&gt;</content>
      <link href="https://bhoot.dev/2022/how-to-mark-up-a-code-snippet-in-html"/>
      <summary type="html">Suppose, we want to mark up the following multi-line snippet of JavaScript code:</summary>
      <category term="HTML" label="HTML" />
<category term="Accessibility" label="Accessibility" />
      <published>2022-10-06T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:8a234ceb-901a-4448-8826-a5e49716c251</id>
      <title type="html">Location of emacs config</title>
      <updated>2022-09-24T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;I prefer to keep user configuration inside the standard &lt;code&gt;XDG_CONFIG_HOME&lt;/code&gt;, which typically points to &lt;code&gt;~/.config/&lt;/code&gt;. Emacs also supports &lt;code&gt;XDG_CONFIG_HOME&lt;/code&gt;, but as the &lt;em&gt;last&lt;/em&gt; entry in its list of preferred locations, where it looks for &lt;code&gt;init.el&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The list goes in the order: &lt;code&gt;~/.emacs.el&lt;/code&gt;, &lt;code&gt;~/.emacs&lt;/code&gt;, or &lt;code&gt;~/.emacs.d/init.el&lt;/code&gt;, and &lt;em&gt;then&lt;/em&gt; &lt;code&gt;~/.config/emacs/init.el&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/emacs/Find-Init.html&quot;&gt;Emacs manual&lt;/a&gt; also says the following:&lt;/p&gt;

&lt;blockquote cite=&quot;https://www.gnu.org/software/emacs/manual/html_node/emacs/Find-Init.html&quot;&gt;
&lt;p&gt;Note also that if neither the XDG location nor &lt;code&gt;~/.emacs.d&lt;/code&gt; exist, then Emacs will create &lt;code&gt;~/.emacs.d&lt;/code&gt; (and therefore use it during subsequent invocations).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, the XDG location, i.e., &lt;code&gt;~/.config/emacs/&lt;/code&gt; wouldn&#39;t typically exist in a system by default. So emacs would always end up defaulting to &lt;code&gt;~/.emacs.d/&lt;/code&gt;. In order to make it read the XDG location, I always had to manually delete &lt;code&gt;~/.emacs.d/&lt;/code&gt; first.&lt;/p&gt;

&lt;p&gt;So much hassle on every new system or after a fresh emacs installation. So I now just &lt;a href=&quot;https://www.gnu.org/software/stow/&quot;&gt;&lt;code&gt;stow&lt;/code&gt;&lt;/a&gt; my emacs dotfiles to &lt;code&gt;~/.emacs.d&lt;/code&gt;.&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2022/location-of-emacs-config"/>
      <summary type="html">After a fresh emacs installation, I always spent a few minutes why emacs wouldn&#39;t pick up my config from &lt;code&gt;~/.config/emacs/init.el&lt;/code&gt;.</summary>
      <category term="Emacs" label="Emacs" />
      <published>2022-09-24T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:49f2ccac-8123-4080-af1c-cd47e7201a75</id>
      <title type="html">What &lt;code&gt;box-sizing&lt;/code&gt; does</title>
      <updated>2022-09-17T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;&lt;code&gt;box-sizing&lt;/code&gt; specifies the part of an element&#39;s CSS box, to which the properties &lt;code&gt;width, min-width,
max-width,
height, min-height, max-height&lt;/code&gt; apply.&lt;/p&gt;

&lt;figure&gt;
    &lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;
p {
    height: 3rem; 
    box-sizing: content-box;
}
&lt;/code&gt;&lt;/pre&gt;
    &lt;figcaption&gt;Result: brown content box is 3rem tall&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;
p {
    height: 3rem; 
    box-sizing: border-box;
}
&lt;/code&gt;&lt;/pre&gt;
    &lt;figcaption&gt;Result: black border box is 3rem tall&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;style&gt;
    figure {
        display: inline flex;
        flex-direction: column;
        gap: 5px;
        margin: 0;
        border: 1px solid #dcdee0;
        line-height: 1;
    }

    figure &gt; * {
        margin: 0;
    }

    figure:first-of-type &gt; figcaption {
        display: inline-block;
        font-weight: 600;
        color: white;
        background-color: brown;
        background-clip: content-box;
        box-sizing: content-box;
        height: 3rem;
        border: 0.5rem solid black;
        padding: 0.5rem;
    }

    figure:last-of-type &gt; figcaption {
        display: inline-block;
        font-weight: 600;
        color: white;
        background-color: brown;
        background-clip: content-box;
        box-sizing: border-box;
        height: 3rem;
        border: 0.5rem solid black;
        padding: 0.5rem;

        margin-top: 1rem;
    }
&lt;/style&gt;</content>
      <link href="https://bhoot.dev/2022/box-sizing"/>
      <summary type="html">&lt;code&gt;box-sizing&lt;/code&gt; specifies the part of an element&#39;s CSS box, to which the properties &lt;code&gt;width, min-width,
max-width,
height, min-height, max-height&lt;/code&gt; apply.</summary>
      <category term="CSS" label="CSS" />
      <published>2022-09-17T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:913be558-883b-446e-ae0d-6b3cf5d18275</id>
      <title type="html">Fake SHA256 in a Nix derivation</title>
      <updated>2022-09-09T11:31:21.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;Discovering the SHA256 hash for a derivation is a chore.&lt;/p&gt;

&lt;p&gt;One approach is to fake the hash with zeroes.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-nix&quot;&gt;sha256 = &quot;0000000000000000000000000000000000000000000000000000&quot;;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This induces nix-build to throw an error containing the correct hash.&lt;/p&gt;

&lt;p&gt;However, even the fake hash has to be correct, i.e., needs to have the right number of zeroes in it. Otherwise,
nix-build throws an error about the hash being invalid instead of being incorrect. This error does not contain the
correct hash.&lt;/p&gt;

&lt;p&gt;Thankfully, Nix already binds the fake hash value to a variable &lt;code&gt;lib.fakeSha256&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-nix&quot;&gt;sha256 = lib.fakeSha256;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://github.com/NixOS/nix/issues/1880#issuecomment-687579758&quot;&gt;paulyoung&lt;/a&gt; for this tip.&lt;/p&gt;

&lt;p&gt;
&lt;ins datetime=&quot;2022-10-03&quot;&gt;I found a more memorable variable &lt;code&gt;lib.fakeHash&lt;/code&gt;.&lt;/ins&gt;
&lt;/p&gt;</content>
      <link href="https://bhoot.dev/2022/fake-sha256-in-a-nix-derivation"/>
      <summary type="html">Discovering the SHA256 hash for a derivation is a chore.</summary>
      <category term="Nix" label="Nix" />
      <published>2022-09-09T11:31:21.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:5e88869a-3274-4b58-9065-8ffcf0dabab7</id>
      <title type="html">How to use &lt;i&gt;Hover Click&lt;/i&gt; without animation in GNOME</title>
      <updated>2020-07-05T11:31:22.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;p&gt;
Desktop systems usually provide an accessibility feature, with which a user only has to hover the cursor
over where he wants to click. The click goes off automatically. In GNOME, this feature is called &lt;i&gt;Hover
Click&lt;/i&gt;.
&lt;/p&gt;

&lt;p&gt;
Not having to press the mouse button makes for a much more pleasurable and efficient experience. Coupled
with &lt;i&gt;Sticky Keys&lt;/i&gt;, &lt;i&gt;Hover Click&lt;/i&gt; has also helped me recover from a hand injury.
&lt;/p&gt;

&lt;p&gt;
The click goes off a few configurable milliseconds after the user positions the cursor. GNOME represents
this buffer with an animating circle, which looks pretty but gets in the way, especially if you are trying
to course-correct the cursor position.
&lt;/p&gt;

&lt;p&gt;
The option to disable this animation is simple but not straightforward. You need to disable all the
animations entirely from Gnome Tweaks:
&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;i&gt;Gnome Tweaks&lt;/i&gt; tool&lt;/li&gt;
&lt;li&gt;Open &lt;i&gt;General&lt;/i&gt; Tab&lt;/li&gt;
&lt;li&gt;Turn &lt;i&gt;Animations&lt;/i&gt; off&lt;/li&gt;
&lt;/ol&gt;

&lt;figure&gt;
&lt;img src=&quot;/static/images/demo-gnome-hover-to-click-animation.gif&quot; alt=&quot;Hover click animation gif demo&quot;&gt;
&lt;figcaption&gt; Hover Click feature in GNOME, with and without animation&lt;/figcaption&gt;
&lt;/figure&gt;</content>
      <link href="https://bhoot.dev/2020/hover-to-click-without-animation-in-gnome"/>
      <summary type="html">Desktop systems usually provide an accessibility feature, with which a user only has to hover the cursor
over where he wants to click. The click goes off automatically. In GNOME, this feature is called &lt;i&gt;Hover
Click&lt;/i&gt;.</summary>
      <category term="GNOME" label="GNOME" />
<category term="Accessibility" label="Accessibility" />
      <published>2020-07-05T11:31:22.000Z</published>
    </entry>
<entry>
      <id>urn:uuid:5f447c9e-6f5d-4cfd-98e8-e917b0429afd</id>
      <title type="html">How to write a Firefox add-on in ClojureScript</title>
      <updated>2020-07-04T11:31:22.000Z</updated>
      <author>
        <name>Jayesh Bhoot</name>
      </author>
      <content type="html">&lt;div class=&quot;toc&quot;&gt;&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of contents&lt;/h2&gt;&lt;ol class=&quot;toc-1&quot;&gt;&lt;li&gt;&lt;a href=&quot;#table-of-contents&quot;&gt;Table of contents&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#org9a73bf2&quot;&gt;Project Layout&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#org4352a07&quot;&gt;ClojureScript to JavaScript&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;#org9d85caf&quot;&gt;Live reload&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;
You can find a sample add-on on my &lt;a href=&quot;https://github.com/jayesh-bhoot/firefox-add-on-in-cljs&quot;&gt;GitHub&lt;/a&gt;.
I won&#39;t go through the code here, but will lay out the quirky development process. I will mostly focus on the
plumbing - how ClojureScript hands over the compiled JavaScript code to the add-on.
&lt;/p&gt;

&lt;p&gt;
The sample add-on tries to mirror the functionality of the one found in the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension&quot;&gt;official
tutorial&lt;/a&gt; - wrap a border around the pages at mozilla.org.
&lt;/p&gt;


&lt;figure&gt;
&lt;img src=&quot;/static/images/demo-ff-addon-cljs.png&quot; alt=&quot;The addon puts a purple border around the current webpage&quot;&gt;
&lt;figcaption&gt;The demo add-on puts a purple border around the current webpage&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;org9a73bf2&quot;&gt;Project Layout&lt;/h2&gt;

&lt;p&gt;
Figuring out a clean project layout took some effort. I am satisfied with the following structure:
&lt;/p&gt;

&lt;pre&gt;project-root
|-- addon  // root for the Firefox add-on
|   |--- manifest.json
|   |--- // add-on artefacts like icons
|   |--- // JS file compiled and placed here by ClojureScript compiler
|-- src  // ClojureScript code
|-- compile-opts.edn  // compilation options provided to ClojureScript compiler
|-- deps.edn
&lt;/pre&gt;

&lt;p&gt;
The goal is to isolate the artefacts of the add-on from the ClojureScript code. The add-on gets only its
JavaScript from ClojureScript. So, isolating the rest of its artefacts - icons, manifest, etc. - into its own
directory works out well. ClojureScript dominates the rest of the project layout.
&lt;/p&gt;

&lt;p&gt;
The only time the ClojureScript compiler touches the &lt;i&gt;addon&lt;/i&gt; folder is to put the compiled JavaScript in
it.
&lt;/p&gt;

&lt;h2 id=&quot;org4352a07&quot;&gt;ClojureScript to JavaScript&lt;/h2&gt;

&lt;p&gt;
The compilation process by default uses &lt;code&gt;:optimizations :none&lt;/code&gt;, which produces a set of JavaScript
files, with the main file pointing at the others. This does not work here as the add-on does not seem to pick up
the secondary files from the main file.
&lt;/p&gt;

&lt;p&gt;
The rest of the &lt;code&gt;:optimizations&lt;/code&gt; options - &lt;code&gt;:whitespace&lt;/code&gt;, &lt;code&gt;:simple&lt;/code&gt;,
&lt;code&gt;:advanced&lt;/code&gt; - produce a single standalone JavaScript file, which the add-on happily accepts.
&lt;/p&gt;

&lt;p&gt;
However, &lt;code&gt;:optimizations :none&lt;/code&gt; is the default because it compiles the fastest, lending a speedy development
process. I went with the second fastest - &lt;code&gt;:whitespace&lt;/code&gt; - for development purpose, and
&lt;code&gt;:advanced&lt;/code&gt; to compile a production build.
&lt;/p&gt;

&lt;p&gt;
Two other compilation options helps keep the plumbing short and clean: &lt;code&gt;:target&lt;/code&gt; diverts the
temporary files produced during compilation to a &lt;i&gt;tmp&lt;/i&gt; folder, while &lt;code&gt;:target-to&lt;/code&gt; puts the
standalone compile JavaScript file directly into the &lt;i&gt;addon&lt;/i&gt; folder.
&lt;/p&gt;

&lt;p&gt;
To summarise, we tweak the default compilation process with the following three options to keep life simple:
&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-clojure&quot;&gt;{:optimizations :whitespace
 :output-dir    &quot;tmp&quot;
 :output-to     &quot;addon/main.js&quot;}&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;org9d85caf&quot;&gt;Live reload&lt;/h2&gt;
&lt;p&gt;
Basically, we instruct the ClojureScript compiler to watch over the ClojureScript files and recompile them into
JavaScript on change.
&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;# in project-root
clj --main cljs.main \
    --watch &quot;src&quot; \
    --compile-opts &quot;compile-opts.edn&quot; \
    --compile demo.core&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
In turn, Mozilla&#39;s web-ext tool by default watches over the add-on folder and reloads the add-on on detecting a
change.
&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;# in addon folder
web-ext run --start-url https://www.mozilla.org/en-US/&lt;/code&gt;&lt;/pre&gt;</content>
      <link href="https://bhoot.dev/2020/firefox-add-on-with-clojurescript"/>
      <summary type="html">I found writing Firefox add-ons to be a good way to learn the ropes in ClojureScript in a productive manner.</summary>
      <category term="Firefox add-on" label="Firefox add-on" />
      <published>2020-07-04T11:31:22.000Z</published>
    </entry>
    </feed>