<rss version="2.0"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <title>kaustubh m.</title>
    <link></link>
    <description></description>
    <language>en-us</language>
    <atom:link href="/rss.xml" rel="self" type="application/rss+xml" />

    <item>
      <title>Abdusattarov - Martinez, Round 3.1, FIDE World Cup, Goa 2025</title>
      <link>/chess/2025-11-abdusattarov-martinez-fide-world-cup-rd-3/</link>
      <guid>/chess/2025-11-abdusattarov-martinez-fide-world-cup-rd-3/</guid>
      <description><![CDATA[<p>[Event &quot;FIDE World Cup 2025&quot;]
[Site &quot;Goa, India&quot;]
[Date &quot;2025.11.08&quot;]
[Round &quot;20.8&quot;]
[White &quot;Abdusattorov, Nodirbek&quot;]
[Black &quot;Martinez Alcantara, Jose Eduardo&quot;]
[Result &quot;0-1&quot;]
[WhiteElo &quot;2750&quot;]
[WhiteTitle &quot;GM&quot;]
[WhiteTeam &quot;Uzbekistan&quot;]
[WhiteFideId &quot;14204118&quot;]
[BlackElo &quot;2644&quot;]
[BlackTitle &quot;GM&quot;]
[BlackTeam &quot;Mexico&quot;]
[BlackFideId &quot;3805662&quot;]
[TimeControl &quot;: 90 minutes for the first 40 moves, followed by 30 minutes for the rest of the gamewith an increment of 30 seconds per move starting from move 1&quot;]
[Variant &quot;Standard&quot;]
[ECO &quot;C41&quot;]
[Opening &quot;Philidor Defense: Exchange Variation&quot;]
[StudyName &quot;Game 1&quot;]
[ChapterName &quot;Abdusattorov, Nodirbek - Martinez Alcantara, Jose Eduardo&quot;]
[ChapterURL &quot;https://lichess.org/study/zIaI233r/KGhZHE5T&quot;]
[Annotator &quot;https://lichess.org/@/kaustico&quot;]
[UTCDate &quot;2025.11.08&quot;]
[UTCTime &quot;07:48:45&quot;]
[Orientation &quot;black&quot;]</p>
<ol>
<li>e4 { [%clk 1:30:52] } 1... d6 { [%clk 1:30:53] } 2. d4 { [%clk 1:30:02] } 2... e5 { [%clk 1:31:17] } 3. Nf3 { The Philidor. I wouldn't play this as black without significant preparation because of the space disadvantage that black accepts in the centre. } { [%clk 1:29:06] } 3... exd4 { [%clk 1:31:41] } 4. Nxd4 { [%clk 1:28:38] } 4... Nf6 { [%clk 1:32:05] } 5. Nc3 { [%clk 1:28:58] } 5... Be7 { [%clk 1:32:31] } 6. Bf4 { [%clk 1:27:25] } 6... O-O { [%clk 1:32:53] } 7. Qd2 { [%clk 1:27:23] } 7... Nc6 { [%clk 1:33:07] } 8. O-O-O { White has developed well now, has control of the centre. All of white's pieces (apart from the Kingside rook) have mobility and are ready to get involved. } { [%clk 1:27:41] } 8... Nxd4 { Black lags in space. The dark-squared bishop is not doing much. Black prefers trading off a good knight to compensate for that. Black must now plan to gain space / improve mobility of their pieces. } { [%clk 1:33:27] } 9. Qxd4 { [%clk 1:28:08] } 9... a6 { Planning to gain space on the queenside. } { [%cal Gb7b5,Gc7c5] [%clk 1:33:51] } 10. f3 { [%clk 1:26:55] } 10... b5 { [%clk 1:32:42] } 11. g4 { [%clk 1:22:00] } 11... c5 { I like this plan for Black. } { [%clk 1:30:05] } 12. Qe3 { [%clk 1:20:58] } 12... b4 { [%clk 1:30:27] } 13. Nd5!? { This is an exchange sacrifice. } { [%clk 1:21:19] } 13... Nxd5 { Black has to trade. Allowing white to take the e7 Bishop is not an option because then the d6-c5-b4 pawns become easy meat. } { [%clk 1:30:22] } 14. Rxd5 { [%clk 1:21:48] } 14... Be6 { [%clk 1:29:30] } 15. Bc4! { [%clk 1:22:04] } 15... Bxd5 { [%clk 1:17:29] } 16. Bxd5 { With Black's light-square bishop gone, white's light-squared bishop has no competition. It is peerless. All the light squares are belong to white.
I would prefer to play white in this position. } { [%clk 1:22:06] } 16... Rc8 { [%clk 1:16:58] } 17. g5 { [%csl Rf7][%cal Gh2h4,Rd5g8,Gh4h5,Gg5g6] [%clk 1:15:01] } (17. Bb7?! { Unnecessary complications and gives Black counterplay. } 17... Rb8 18. Bxa6 g5 19. Bg3 Bf6 20. Rd1 Bd4 21. Rxd4 cxd4 22. Qxd4) 17... c4 { [%clk 1:17:22] } 18. Kb1 { [%clk 1:11:46] } (18. h4 { Is also good for white because h5-g6 comes fast and white's pieces are capable of guarding the king. } 18... Qa5 19. Kb1 c3 20. h5 cxb2 21. g6 Rc3 22. Qd2) 18... Qa5 { [%clk 1:08:31] } 19. h4 { [%clk 1:09:07] } 19... Rc5 { [%clk 1:07:02] } 20. Qd4 { [%clk 1:04:36] } (20. h5 { was also okay } 20... Rxd5?? { does not give black anything in return. } 21. exd5 { [%csl Ge7][%cal Ge3e7] }) 20... Rfc8?! { Inaccuracy. Rd8 was best. } { [%clk 0:33:04] } (20... Rd8) 21. h5 { [%clk 0:59:35] } 21... Rxd5 { [%clk 0:22:08] } 22. exd5 { White's earlier plan was centred around the light squared bishop. With that gone, there needs to be a new plan. White has a lot of space on the kingside. With Black's dark bishop unable to enter the game, I don't see a queenside threat for White.
If I was white, my plan would center around making use of the kingside pressure and the open e-file. } { [%clk 0:59:59] } 22... Bf8 { [%clk 0:22:02] } (22... Qc7? { If not ...Bf8, then } 23. h6 Bf8 24. hxg7 Bxg7 25. Qe4) 23. h6?! { Inaccuracy. g6 was best. } { I don't like this move because it solidifies the kingside structure. The d6 pawn is defended by the Bishop. The Queen and Rook can come in and White cannot add pressure there. I like g6 more. } { [%clk 0:48:02] } (23. g6 fxg6? 24. Qe4 Qd8 25. Qe6+ Kh8 26. hxg6 h6) 23... Qc5 { [%clk 0:17:24] } 24. Qe4 { [%clk 0:48:26] } 24... g6 { [%clk 0:17:50] } 25. Be3 { [%clk 0:45:48] } 25... Qb5 { [%clk 0:17:48] } 26. Bd4? { Mistake. Qd4 was best. } { White's idea here must have been Bd4, Qf4-Qf6 to create deadly threats. White must've underestimated Black's counter-play paired with the defensive idea of f5.
The d5 pawn is important but weak. } { [%clk 0:45:14] } (26. Qd4! Rc5 (26... Re8 27. Bf4 Qc5 28. Qxc5 dxc5 29. Rd1 $16) 27. Qf6 Rxd5 28. Bd4 Rxd4 29. Qxd4 $16) 26... Re8 { [%clk 0:17:15] } 27. Qf4 { [%clk 0:40:18] } 27... f5 { [%clk 0:17:40] } 28. gxf6 { [%clk 0:36:41] } 28... Qxd5 { Maybe what Nodirbek missed in his calculations? Taking the pawn also defends the f7 square against f7+ } { [%clk 0:17:54] } 29. b3 { [%clk 0:37:01] } 29... Kf7 { [%clk 0:13:06] } 30. Qg4 { [%cal Gg4d7] [%clk 0:35:53] } 30... Qf5 { [%clk 0:11:40] } 31. Qg2 { [%clk 0:27:30] } 31... c3 { [%clk 0:09:09] } 32. a4 { [%clk 0:25:55] } 32... bxa3 { [%clk 0:08:52] } 33. Ka2 { [%clk 0:24:17] } 33... a5 { [%clk 0:06:56] } 34. Rh4?? { Blunder. Qf2 was best. } { [%clk 0:14:41] } (34. Qf2 Rc8 (34... a4 35. Bxc3 axb3+ 36. cxb3 Qd3?? { The same idea does not work in this line because White gets counter-play with the queen able to participate in an attack against the Black king. } 37. Qa7+ $18) 35. Ka1 a4 36. bxa4 Rc4 37. Rd1 Qd5 38. Qe3 Rxa4 39. Rd3 Qb7) 34... a4 { [%clk 0:04:15] } 35. Bxc3?? { Blunder. Re4 was best. } { [%clk 0:15:05] } (35. Re4 axb3+) 35... axb3+ { [%clk 0:04:39] } 36. Kxb3 { [%clk 0:12:38] } (36. cxb3 Qd3 { Double attack } { [%csl Ge2,Gc3][%cal Ge8e2,Gd3c3] }) 36... Rb8+ { [%clk 0:03:36] } 37. Rb4 { [%clk 0:11:52] } 37... Qe6+ { [%clk 0:03:51] } 38. Kxa3 { [%clk 0:12:14] } 38... d5 { [%clk 0:04:04] } 39. Qf1?? { Checkmate is now unavoidable. Qg4 was best. } { [%clk 0:08:21] } (39. Qg4 Bxb4+ 40. Bxb4 Qxg4 41. fxg4 Kxf6 42. Bd2 Ke5 43. Be3 d4 44. Bd2 Kd5) 39... d4 { [%clk 0:04:02] } 0-1
</li>
</ol>
]]></description>
      <pubDate>2025-11-08T00:00:00+05:30</pubDate>
    </item>
    <item>
      <title>Progress Notes: raga - Static Site Generator</title>
      <link>/notes/raga-ssg/</link>
      <guid>/notes/raga-ssg/</guid>
      <description><![CDATA[<p>This document serves as a progress log of my work on <code>raga</code> - a static site generator in <a href="https://ocaml.org">OCaml</a>.
This is also the project I want to submit as my capstone project for the
<a href="https://www.coursera.org/degrees/bachelor-of-science-computer-science-bits">Computer Science course I am enrolled in</a>.
This document is organised as a short description of the project, followed by a reverse-chronological
list of progress notes.</p>
<h2>About <code>raga</code></h2>
<p>This is the proposal I had drafted in June 2025: <a href="https://docs.google.com/document/d/1XNGjF7BY2bIqDLiNVjuVsu28S2vniuOH/edit">project proposal</a>.
The project was initially called <code>pushpush</code>, but I've since decided to rename it to <code>raga</code>.</p>
<p>The idea is to make a static site generator in OCaml that is capable enough to generate &amp; deploy my
own website + <a href="https://kaustubh.page/posts/my-personal-website-philosophy/">some other ideas</a> that I have.
I also want to use this as an excuse to write non-trivial software with OCaml--and in the process find out
what is easy / hard to do for me in OCaml.</p>
<p>The project was planned to have 2 parts for me to build:</p>
<ol>
<li>[done] A <a href="https://handlebarsjs.com/">Handlebars</a> templating engine
</li>
<li>[done] The actual static site generator that would make use of the templating engine
</li>
</ol>
<p>Since then, a third got added and completed, which is a parser for the <a href="https://huml.io">huml</a>
configuration language.</p>
<h3>Links</h3>
<ol>
<li><code>raga</code>:
<ul>
<li><a href="https://github.com/nikochiko/raga"><code>nikochiko/raga</code></a>: GitHub repository
</li>
<li>This website was built with <code>raga</code>.
</li>
</ul>
</li>
<li><code>handlebars-ml</code>:
<ul>
<li><a href="https://github.com/nikochiko/handlebars-ml"><code>nikochiko/handlebars-ml</code></a>: GitHub repository
</li>
<li><a href="https://opam.ocaml.org/packages/handlebars-ml/"><code>opam:handlebars-ml</code></a>: <code>opam</code> package
</li>
<li><a href="https://handlebarsjs.com">handlebarsjs.com</a>: Original (JavaScript) implementation of handlebars
</li>
</ul>
</li>
<li><code>huml-ml</code>:
<ul>
<li><a href="https://github.com/huml-lang/huml-ml"><code>huml-lang/huml-ml</code></a>: GitHub repository
</li>
<li><a href="https://huml.io">huml.io</a>: huml-lang official website
</li>
</ul>
</li>
<li>Project Proposal: <a href="https://docs.google.com/document/d/1XNGjF7BY2bIqDLiNVjuVsu28S2vniuOH/edit">Google Docs</a>
</li>
</ol>
<h2>23 November 2025</h2>
<h3>raga</h3>
<h4>Usability testing</h4>
<h5>Adding chess games to website</h5>
<p>I implemented a <a href="https://en.wikipedia.org/wiki/Portable_Game_Notation">PGN</a>
template for my own website to test the capabilities of <code>raga</code>. I had to allow
front-matter in non-markdown files to make this work, but it works quite well.
For example, this is the chess games list on my website: <a href="/chess">/chess</a>.</p>
<p>I'm not particularly pleased with this approach because I need to replicate
metadata information that is already there in the PGN into the front-matter.
This is not strictly needed because it can be done from JavaScript, but this
helps SEO bots and RSS feeds.</p>
<h5>Building template with Unicode website</h5>
<p>I did some more testing with trying to implement a website template with
Unicode in the configuration. That case also works well. I ran into some
problems with setting up the default case. I've added
<a href="https://github.com/nikochiko/raga/issues">GitHub issues</a> to track my work on
this task.</p>
<p>The setup task is also slightly long in the sense that I need to setup
the directory structure. I also discovered a bug here that expects the
partials directory to always be present. I want to change that to
throw a warning in that case.</p>
<h4>Feature - Build summary</h4>
<p>I added a feature to the CLI to display useful build stats at the end of
every build. This gives some visibility into what was generated:</p>
<p><img src="/media/build-summary.png" alt="build-summary" /></p>
<p>It also shows how long it took to build something. I like the current
build times too.</p>
<h4>publishing to opam</h4>
<p>raga has now been published to opam. I will start talking about it
and provide a single binary for installation after this round of
fixes and some testing.</p>
<h3>handlebars-ml</h3>
<p>I'm quite happy with how stable this has been. I want to refactor
the helper functionality to:</p>
<ol>
<li>have more power, so that for example one helper can be used to
add the boilerplate HTML for enclosed markdown content.
</li>
<li>give better errors and warnings when the arguments passed are
incorrect (for example, when they have wrong types).
</li>
</ol>
<h3>huml-ml</h3>
<p>I recently worked on the BNF for this and we have a new spec with a
few changes that I want to update the OCaml and other parsers for.</p>
<h3>TODOs:</h3>
<ul>
<li>latex support for math notation (find a page that needs this)
</li>
<li>update huml parser for 0.2 spec
</li>
<li>only throw warning when partial directory does not exist
</li>
<li>throw warnings for incorrect helper arguments
</li>
<li>refactor helpers to be more powerful
</li>
</ul>
<h2>03 November 2025</h2>
<h3>raga</h3>
<p>I got started and have a working <code>raga</code> implementation. This website
now runs on <code>raga</code> instead of <code>hugo</code>. I used the same content files for the
most part, with some minor changes, and I was able to build a somewhat
large website with it. This gives me confidence in its power. While
working on it, I found some bugs/improvements in the handlebars implementation
that I then fixed.</p>
<p>I think I've come up with an interesting config solution that works
kind of like a pipeline-definition language. I define the source files,
the processing to be done on them, and the place to output them.
I'm working mostly with markdown and have frontmatter support for exclusion
rules. I also want it to be a general-ish pipeline tool that can let me
build arbitrary files. For example, I want to take my PGN files that contain
chess analysis, and publish them as HTML pages with the correct transformations.
Adding new functionality is done with <code>OCaml</code> code at the moment. I might
want to change that to be able to generalise to bash scripts at some point.
I'm also getting the feeling that perhaps s-expressions might have been
a nicer, powerful format for the config and that I'm trying to emulate
them into huml for the config syntax.</p>
<p>I have been able to add all the features from <a href="#raga">the previous wishlist</a>,
and more. I'm happy about that.</p>
<p>State so far:</p>
<ul>
<li>working <code>raga</code> static site generator implementation
</li>
<li>config parsing is strict--will error for bad key/values.
</li>
</ul>
<p>TODOs:</p>
<ul>
<li>compile a non-markdown file with raga (e.g. <code>PGN</code> with chess files)
</li>
<li>add support for latex
</li>
</ul>
<h2>18 October 2025</h2>
<h3>handlebars-ml</h3>
<p>I have been able to get to a version of <a href="https://github.com/nikochiko/handlebars-ml"><code>handlebars-ml</code></a> that I feel
comfortable using. I have also <a href="https://opam.ocaml.org/packages/handlebars-ml/">published it to the <code>opam</code> package manager</a>.
I have added enough tests for me to feel comfortable with the featureset that is in there.
There is support for helpers, partials, and all the basics. Inline partials are not yet supported
but I am okay with that because the same usecase is possible with workarounds.</p>
<p>Some areas where I feel like improvements are possible are:</p>
<ul>
<li>better support for date &amp; time parsing / rendering. I don't want to bloat the templating engine, so I'm thinking this
can instead be part of <code>raga</code>.
</li>
<li>the CLI is not enough on its own to support adding new helpers. As of now, the workflow for that use case is to use the
library, write OCaml code for the helpers and compile that. I think I'm okay living with this for the moment. I have been
considering small scripting languages for this use case though.
</li>
</ul>
<h3>huml-ml</h3>
<p>Fresh off writing a new parser for handlebars, I got tempted into writing another one for a new &amp; strict configuration language
called <a href="https://huml.io">huml</a>. I find it interesting and useful. I think I want to add support for parsing frontmatter
and configuration in <code>huml</code>. Maybe even start with it as the default.</p>
<p>I wrote about it to <a href="https://nadh.in">Kailash</a>, a maintainer of this project, who was happy to see it. Since then, I have moved
the implementation upstream to the official huml org. I am also working on a BNF grammar for it in addition to some bug fixes in
other implementations. I have been enjoying this. I enjoy writing parsers and this kind of working with the community makes me
feel useful.</p>
<p>(I have oddly enough written &gt;3 parsers, which I think is more than the number of parsers that most engineers are likely to write).</p>
<h3><code>raga</code></h3>
<p>Per the schedule, I think I'm running ahead on the <code>handlebars-ml</code> work and just-on-time on <code>raga</code>. I have a design architecture
in mind. I think the goal I have in mind--to be able to compile my own website--keeps me grounded in terms of what I need
to build.</p>
<p>For the first version, I have in mind that it should work somewhat similarly to <a href="https://gohugo.io"><code>hugo</code></a>, which is the
generator that I currently use.</p>
<p>Specifically, it should be this featureset:</p>
<ul>
<li>Parse frontmatter in <code>huml</code> and pass that to the theme/template
</li>
<li>Parse config in <code>huml</code> and pass that to the theme/template
</li>
<li>Allow generic templates in a theme (e.g. <code>_blog.html</code> that will work as one template for all blogs).
</li>
<li>Translate markdown content to <code>html</code>
</li>
<li>Make a template for the RSS feed
</li>
</ul>
<p>I plan to work on this in the next couple of weeks.</p>
]]></description>
      <pubDate>2025-10-18T13:06:17+05:30</pubDate>
    </item>
    <item>
      <title>Weeknote 04 October 2025</title>
      <link>/posts/weeknote-04-october-2025/</link>
      <guid>/posts/weeknote-04-october-2025/</guid>
      <description><![CDATA[<h2>handlebars-ml</h2>
<p>My personal celebration this week is that I did released
the <a href="https://github.com/nikochiko/handlebars-ml">OCaml handlebars templating library</a>
that I was working on. I felt happy when I read the &quot;Thanks&quot;
and &quot;Your PR ... has been merged&quot; emails for publishing to
the opam repository. This can now be installed with <code>opam install handlebars-ml</code>.</p>
<p>I think this is software that I am proud of. I have tested it enough for me
to feel confident about relying on it. I should likely do a more detailed write-up
about this because I did learn several things in the process of making it:
<code>ocamllex</code>, <code>menhir</code>, <code>sedlex</code>, when <code>claude code</code> was useful, how opam publishing
works, yada yada. Even the opam CI had an interesting, thorough testing process--for
example that the project gets built &amp; tested on different OCaml versions and with the
latest &amp; lower-bound versions of the dependencies. This is a new testing model that I
will carry with me.</p>
<p>I feel more fondly about <code>handlebars</code> than I did at some point during the making of
it. My confusion/distaste plotted against time would be a monotonously increasing graph
with a sudden drop from the peak close to completion. It has pitfalls and semantics
that silently ignore where things should probably crash soon. That said, I feel
comfortable enough with those to be able to make use of the convenience features
that sit on top. Perhaps I should add optional warnings in these cases to compensate.
In any case, both the good &amp; bad of this deserve a more detailed write-up. In summary,
I feel happy about having completed this. I am also happy about the error-reporting
&amp; CLI interface that I built in, and so I would in fact like to make use of it in my own
projects.</p>
<h2>huml</h2>
<p>I am procrastinating on the category of activities that this writing would also fall
into. I am bike shedding on the static site generator that the templating engine
is supposed to be a part of. My latest side quest after being satisfied with
the handlebars parser was to perhaps write another OCaml parser--this time for
a configuration language called <a href="https://huml.io">huml</a>. huml stands for
HUman-oriented Markup Language, and is an experiment by <a href="https://nadh.in">kailash</a>
to make a replacement for YAML. I agree with the dislike of YAML. I do sometimes
find TOML less than easily readable. I think that is because the hierarchy
is not obvious from the visual representation of the text as it is in YAML.
I have made some progres on this. If I do get to a version that works with the
tests that are put up, then I will very likely consider using it for my static
site generator and writing to kailash about it.</p>
<p>I also really like that there is a <a href="https://github.com/huml-lang/tests">language-neutral test set</a>
for it, so I can actually verify that my implementation works. This is great
for times when the specification is ambiguous. I think a formal grammar
specification (a BNF) would also perhaps be useful towards that end.</p>
<h2>life</h2>
<p>I had fun making a short video in the format of old good-morning photos with
hindi songs for a friend's birthday. I did that in p5.js and took help of
the silicon-family techbros to get there. I am amazed again about how
powerful p5 is. I will likely consider making a tool to make it easier
to get GIFs of animations or fix the <code>saveGIF</code> method they had because
it did not work for me. I ended up doing a screen recording at the end
and then stitch that with an audio file with <code>ffmpeg</code>.</p>
<p>I do enjoy playing around in the command line. It is satisfying to write
an <code>ffmpeg</code> command and have it do the thing you want. I think of the
SICP-ian analogy of the wizard's incantations.</p>
<p>I also visited the Indian Music Experience museum this week which was
absolutely beautiful. I would like to share some pictures from the visit
in a post.</p>
<h2>website</h2>
<ul>
<li>I fixed my website's RSS feed thanks to <a href="https://abhinavsarkar.net">Abhinav</a>'s
nudge.
</li>
<li>should absolutely get started on the static site generator
</li>
<li>need to fix my infra. i have setup comments but they don't show up on my posts
because my laptop is turned off and i need to change the boot order to get it back
up and runnning as a homeserver.
</li>
</ul>
<p><em>more to be added?</em></p>
]]></description>
      <pubDate>2025-10-04T17:23:44+05:30</pubDate>
      <category>weeknotes</category>
      <category>personal</category>
    </item>
    <item>
      <title>Locked Myself Out of a Machine. Again.</title>
      <link>/micros/locked-myself-out-of-a-machine-again/</link>
      <guid>/micros/locked-myself-out-of-a-machine-again/</guid>
      <description><![CDATA[<p>Today, I locked myself out of a remote machine. Again...
An SSH misconfiguration this time. I was sure I had placed
the public keys in the correct place for the new user.
I was sure that the permissions were set correctly, so it
would be safe to disable remote root login that the default
instance came setup with.</p>
]]></description>
      <pubDate>2025-09-09T17:27:28+05:30</pubDate>
    </item>
    <item>
      <title>Papers</title>
      <link>/papers/</link>
      <guid>/papers/</guid>
      <description><![CDATA[<p>Reading list for papers.</p>
<h1>September 2025</h1>
<ol>
<li><a href="/media/iverson.pdf">Notation as a Tool of Thought, Iverson (1979)</a>
</li>
</ol>
]]></description>
      <pubDate>2025-09-09T16:31:47+05:30</pubDate>
    </item>
    <item>
      <title>My Personal Website Philosophy</title>
      <link>/posts/my-personal-website-philosophy/</link>
      <guid>/posts/my-personal-website-philosophy/</guid>
      <description><![CDATA[<!-- I want my personal website to mirror me hosting someone at my home - to the extent
that I can. If I am having someone over at my place, I find them interesting. There
is generally a finite range to the conversational topics or interactions that I
like. I can provide a window to some of those via my website.

I think a few elements of that set would be:
- *Oh, did I tell you that I self-host all my websites from home? See, right **there**.* 
- *I've been working on this really cool project. It's still a work-in-progress but I can demo it for you **here**.*
- *I did something funky in the coffee making process and it came out really well. I want you to try it.*
- *If you are amenable to solving a chess puzzle, I would like to show you some insane ones I recently chanced upon.* -->
<p>I want my personal website to mirror - to the extent that I can - the experience that you
would have if we met in a warm setting. That warm setting can be imagined as the range between
me hosting someone at mine to us sharing a common interest; perhaps better described as
an interaction that is somewhat friendship-shaped.</p>
<p>Depending on what is the intersectionality in the venn diagram of our interests, I could
be taking about one of a few things (non-exhaustive):</p>
<ul>
<li><em>I self-host all my websites from home and actually from <strong>that</strong> laptop there</em>
</li>
<li><em>I've been working on this really cool project. It's still a work-in-progress but I can give you a demo here.</em>
</li>
<li><em>I ran into this problem at work recently. This is how we fixed it.</em>
</li>
<li><em>I did something funky in my coffee making process and the result was interesting. I want you to try this out.</em>
</li>
<li><em>I analyse chess games sometimes and I would like you to go through them too and let me know if you have
thoughts somewhere. I can also analyse one of your games.</em>
</li>
</ul>
<p>I see that just text is often not the right medium. I want my web space to be more dynamic. I want to be
able to render chess puzzles and analyses of chess games here too. I want to share opinions on coffee,
tech, travel, etc. and the more meta aspects of them. I also want it to be such that when you are on
my website, you can navigate to the interactions and conversations that are interersting to you - and
filter out the rest.</p>
]]></description>
      <pubDate>2025-07-19T12:30:19+05:30</pubDate>
      <category>personal</category>
    </item>
    <item>
      <title>Explorations in Creative Programming</title>
      <link>/posts/explorations-in-creative-programming/</link>
      <guid>/posts/explorations-in-creative-programming/</guid>
      <description><![CDATA[<p><img src="/photos/creative-coding-heart.png" alt="String Art 1" />
<img src="/photos/creative-coding-simple.png" alt="String Art 2" /></p>
<p><em>Creative Programming / Creative Coding</em></p>
<p>Here's what's been on my mind:</p>
<ul>
<li>making a programming language for origami is hard.
</li>
<li>string art is cool and can be replicated in ocaml-joy.
</li>
<li>comparisons with music
</li>
</ul>
<p>This is something I've been fascinated by since a young age. This was
my first experience with programming that I liked and wanted to do more
of. Of relatively late, I have been working with <a href="https://github.com/sudha247/ocaml-joy">ocaml-joy</a>
(hosted on <a href="/joy/">kaustubh.page/joy</a>) where I had last explored
tessellation art.</p>
<p><img src="/photos/creative-coding-tessellation-2.png" alt="Tessellation 2" /></p>
<p>While in a fit of boredom, I remembered that <a href="https://anandology.com">@anandology</a>
had once described his explorations with string art. I took inspiration and started
hacking on OCaml Joy's online editor. The result was this <a href="https://is.gd/zyj4Bg">starter code</a>
that comes with a few abstractions to emulate a nailboard and threads. It's rough around
the edges, but I was able to take an <a href="https://www.instructables.com/String-Art-Heart-Strings/">instructables post</a>
and reliably see it in my canvas (see the heart photo at the top).</p>
<p>Along similar lines, I have Wolfram's <a href="https://www.wolframscience.com/nks"><em>A new kind of science</em></a>
on my reading pile. That book is full of interesting visualizations that I want to
get to.</p>
<hr />
<p>I can't help but see the similarities between string art and music. There's a period
with which patterns repeat themselves (<em>a bar</em>). There is a play on how big each step
is and that changes the output you will find.
Two interesting explorations come to mind here:</p>
<ol>
<li>conversions for these art steps to music exist and must surely sound good
(time to test the hypothesis)
</li>
<li>a program that uses code to represent a certain visual animation is a program that
represents a certain musical composition. what combination will look and sound the best?
</li>
</ol>
<p>All of this is another way to say that <em>visual art, when abstracted enough</em> - has similarities
to <em>musical art, when abstracted enough</em>. In math, we do this all the time. Abstract
something in one place (say, counting -&gt; numbers) and then play with that abstraction
until we end up with natural language (LLMs) or even utter nonsense such as a complex plane
and the Mandelbrot fractal.</p>
<hr />
<p>Besides this, I also (briefly) felt the motivation to bulid a programming language for
origami. A way to describe the folds unambiguously, such that an interpreter
will know for sure the state of the paper and be able to create a 3D visualization.</p>
<p>The more I thought about it, the more I realised the difficulty of this problem.
How do we describe a fold in a way that feels more or less natural to a human
and is at the same time unambiguous? An instruction as simple as &quot;Fold such that
corner P aligns with corner Q&quot; can have multiple ways of being done in 3D space.
It gets more complicated with more folds. How do we keep track of the state?
How does the user identify one edge from another? A single fold can create
multiple new edges and corners in a way that isn't easy to track without an origami REPL.</p>
<p>This is a problem that, nonetheless, is interesting to me. It feels like a challenging
exercise in a way that is comparable to a hard, multi-day trek.</p>
<hr />
<ul>
<li><a href="https://tixy.land">tixy.land</a>
</li>
<li>similar to the origami language, can there be an unambiuguous language to describe
crocheting?
</li>
<li><a href="https://en.wikipedia.org/wiki/Spirograph">spirographs</a>
</li>
<li>how do i extend the string art abstractions to accommodate more complex nail-shapes - such as the flower hole pattern?
</li>
</ul>
]]></description>
      <pubDate>2025-02-11T05:53:10+05:30</pubDate>
      <category>creative coding</category>
      <category>tech</category>
      <category>ocaml</category>
    </item>
    <item>
      <title>Tradeoff Between Types and Devspeed</title>
      <link>/micros/tradeoff-between-types-and-devspeed/</link>
      <guid>/micros/tradeoff-between-types-and-devspeed/</guid>
      <description><![CDATA[<p>It seems as if there is always a trade-off between predictability with types when
writing code and the speed at which this code can be written. I'm writing about
programming languages, as developer tools.</p>
<p>Think about Rust, OCaml, even typed-Python. One is easier to implement and one
is more robust to bugs. You don't want to launch a software 100% free of bugs
after it has become obsolete. At the same time, you also don't want to launch
a software so premature that it is as reliable as the flip of a coin.</p>
<p>There are two kinds of software here that change which side of the trade-off you
would rather be on:</p>
<ul>
<li>The fresh new startup kind: you want to be the first to market. It's probably
okay if you have a few bugs on the edge cases, but the fear of becoming obsolete
before delivery is stronger. You can also improve the product from its users
after the launch. You should be more afraid of not having any users by the time
you launch.
</li>
<li>The incremental featureset kind: you're building a new component to a software
that already exists and has users. You won't lose all your revenue if you are
late by a few weeks. You will, however, lose revenue if your new commit causes
the website to crash for some paying customers.
</li>
</ul>
<p>My hypothesis is that when starting out, you want to build for speed. Once built,
you want to focus on making it better. An approach that gives you bug-free software
from the ground up will take more time from the beginning. Instead, the ideal
approach would let you start loose and then make your software more and more
robust as your company matures. No programming language factors this into its
construction. Most companies will just rewrite at some point.</p>
<p>I guess you want the best of both worlds and the ability to move slowly from one
edge to the other. I'm starting to appreciate Python for this.</p>
]]></description>
      <pubDate>2024-07-22T23:53:01+05:30</pubDate>
    </item>
    <item>
      <title>Vim</title>
      <link>/posts/vim/</link>
      <guid>/posts/vim/</guid>
      <description><![CDATA[<p>I started Vim several years ago. My first deliberate learning
came one random day when I had some free time and had recently
learnt that Vim tutor exists.</p>
<p>I completed the exercises. The idea of using a keyboard to do
complicated editor actions - things like skipping through
the start and end of <em>English words</em>, going to the beginning and
end of lines, skipping to the next empty line - felt powerful.
I have since then, through recommendations, been a full Neovim
user. I would hate being in a world that doesn't have Vim
navigation. I felt compelled to install Vimium in the browser
too.</p>
<p>I have however also been bad at keeping up with all the extensions.
I don't want to concern myself with what treesitter does and
what extension I need to lookup the signature of a function. For
over 2 years, I used a Neovim with only syntax highlighting and
nvim-tree as my primary editor. I would use third-party tools
from the terminal for any checks. This put me at a disadvantage
and I realised this when I looked at the workflows of my friends.</p>
<p>About 5 months ago, I made the switch to VSCode with all the
important extensions. I started using the mouse too often.
I was using Copilot's Chat, inspecting the function signature
when writing code, a shortcut for auto-formatting on close, and
vim mode of course.</p>
<p>I convinced myself to like it, and for a while I really did.
I could do so much more with my editor. I definitely was
hopping around different files less. I found the codebase-wide
grep with ctrl+shift+F too useful. I did not have (or know?)
that in Neovim. Yet, everytime I moved my hand to use the mouse,
and everytime it was stuck, I got the feeling that isn't made
for me. It didn't take my personality into account. I felt
distant. The Vim mode was not enough. Me setting keybindings
was not enough. The final blow came when I moved houses and
had a new desk setup. I did not have a mouse yet and had
elevated my laptop. It was too inconvenient to use the touchpad.
Around the same time, I was also being frustrated regularly
with VSCode being stuck. That was probably some extension that
was using too much compute. In any case, and as you might guess
by now, I am too lazy to find out. It's easier to go back to
Neovim.</p>
<p>This time, I installed LunarVim - a project that comes with all
the extensions installed. The transition from a vanilla Neovim
to VSCode to a fully-setup Neovim gave me so much more context
on what the extensions are doing. I am still unwilling to dig
into the specific extensions, but I am more aware of the kind
of workflow that would make me faster. I loved LunarVim. I loved
being back at the terminal to write in Vim. I love not needing
to use my mouse.</p>
<p>I got the feeling that I was at home. I'm not switching back
to VSCode now. This is good. I'm adding more extensions.
I'll read up a bit on the extensions (I'll hesitate before
I do that, only not as much as before). This is good!</p>
]]></description>
      <pubDate>2024-04-14T18:00:13+05:30</pubDate>
      <category>tech</category>
    </item>
    <item>
      <title>What I&apos;m doing now</title>
      <link>/now/</link>
      <guid>/now/</guid>
      <description><![CDATA[<p>(This is a <a href="https://nownownow.com/about">now page</a>, an idea I stole
from <a href="https://sive.rs">sive.rs</a>).</p>
<p><em>Last updated 23 Novemeber 2025</em></p>
<h2>raga (static site generator)</h2>
<p>I've been working on raga, handlebars-ml, and huml-ml. Three OCaml
projects that are all used in building my website now. I'm writing
progress notes about it <a href="/notes/raga-ssg/">here</a>.</p>
<h2>kaafi hai</h2>
<p>I've been talking with some friends about <em>kaafi hai</em> (translation: is good enough)
technology. This class of technology is old, well-tested, stable, boring tech
components that work and scale. They have a low <a href="https://mcfunley.com/choose-boring-technology">innovation token</a>
cost. Think: PostgreSQL, Ruby on Rails, Django, RabbitMQ, Redis, Docker, Ansible.
Counter-examples are SaaS-run, highly-marketed tools: MongoDB, graph databases,
<em>serverless</em> functions, (to some extent) Kubernetes, GraphQL API gateways.</p>
<p>To be fair, I've played with all of these tools and sometimes there are use cases
where they work better than old tech and are also worth the effort of maintaining it.
At Gooey.AI, we use Kubernetes so that we can autoscale on a k8s cluster and have it
scale down when usage is low. GPUs are expensive and this ends up saving us a lot
of money. We don't use k8s for anything else in our deployment story.</p>
<p>But the use cases these tools are marketed to are far more than the use-cases they
were built for. They end up costing the adopting org in money &amp; effort in the
future. Most of their user base is hype-driven. I want to write, talk about, and
evangelise the other idea that does not get VC funding.</p>
<p>I absolutely love Postgres. Considering doing something with that information.</p>
<hr />
<p>Content below was written on 4 October 2025.</p>
<h2>handlebars-ml</h2>
<p>My personal celebration this week is that I did released
the <a href="https://github.com/nikochiko/handlebars-ml">OCaml handlebars templating library</a>
that I was working on. I felt happy when I read the &quot;Thanks&quot;
and &quot;Your PR ... has been merged&quot; emails for publishing to
the opam repository.</p>
<p>I think this is software that I am proud of. I have tested it enough for me
to feel confident about relying on it. I should likely do a more detailed write-up
about this because I did learn several things in the process of making it:
<code>ocamllex</code>, <code>menhir</code>, <code>sedlex</code>, when <code>claude code</code> was useful, how opam publishing
works, yada yada. Even the opam CI had an interesting, thorough testing process--for
example that the project gets built &amp; tested on different OCaml versions and with the
latest &amp; lower-bound versions of the dependencies. This is a new testing model that I
will carry with me.</p>
<p>I feel more fondly about <code>handlebars</code> than I did at some point during the making of
it. My confusion/distaste plotted against time would be a monotonously increasing graph
with a sudden drop from the peak close to completion. It has pitfalls and semantics
that silently ignore where things should probably crash soon. That said, I feel
comfortable enough with those to be able to make use of the convenience features
that sit on top. Perhaps I should add optional warnings in these cases to compensate.
In any case, both the good &amp; bad of this deserve a more detailed write-up. In summary,
I feel happy about having completed this. I am also happy about the error-reporting
&amp; CLI interface that I built in, and so I would in fact like to make use of it in my own
projects.</p>
<h2>huml</h2>
<p>I am procrastinating on the category of activities that this writing would also fall
into. I am bike shedding on the static site generator that the templating engine
is supposed to be a part of. My latest side quest after being satisfied with
the handlebars parser was to perhaps write another OCaml parser--this time for
a configuration language called <a href="https://huml.io">huml</a>. huml stands for
HUman-oriented Markup Language, and is an experiment by <a href="https://nadh.in">kailash</a>
to make a replacement for YAML. I agree with the dislike of YAML. I do sometimes
find TOML less than easily readable. I think that is because the hierarchy
is not obvious from the visual representation of the text as it is in YAML.
I have made some progres on this. If I do get to a version that works with the
tests that are put up, then I will very likely consider using it for my static
site generator and writing to kailash about it.</p>
<p>I also really like that there is a <a href="https://github.com/huml-lang/tests">language-neutral test set</a>
for it, so I can actually verify that my implementation works. This is great
for times when the specification is ambiguous. I think a formal grammar
specification (a BNF) would also perhaps be useful towards that end.</p>
<h2>life</h2>
<p>I had fun making a short video in the format of old good-morning photos with
hindi songs for a friend's birthday. I did that in p5.js and took help of
the silicon-family techbros to get there. I am amazed again about how
powerful p5 is. I will likely consider making a tool to make it easier
to get GIFs of animations or fix the <code>saveGIF</code> method they had because
it did not work for me. I ended up doing a screen recording at the end
and then stitch that with an audio file with <code>ffmpeg</code>.</p>
<p>I do enjoy playing around in the command line. It is satisfying to write
an <code>ffmpeg</code> command and have it do the thing you want. I think of the
SICP-ian analogy of the wizard's incantations.</p>
<hr />
<p>Content below was written on  19 July 2025.</p>
<h2>July 2025 Update</h2>
<p>I am writing this from the <a href="https://underline.center/t/indiewebclub-4-with-ankur-and-tanvi/371">IndieWebClub</a>
meetup in Bengaluru, which is about personal websites. That finally gave me enough push
to update this website. In the past few months, I've been wanting to write
and update this website. I've wanted to make my website taste more like me.</p>
<p>I've also been working on a static site generator that I'm pretty excited about.
I'm building it from its core - starting from the lexer for my templating engine.
It's a good technical exercise. Until then, most of my writings are postponed.</p>
<p>Besides the tooling update, I've also been having ideas about what I want the
website to look like - in a way that impacts the content and design. It's
probably best if I make a separate post out of it. That also makes more sense
to me because I'll have a written philosophy to drive my building. ~~Please
threaten me if you don't see a link here.~~ This
<a href="https://kaustubh.page/posts/my-personal-website-philosophy/">here</a>
is the link.</p>
<hr />
<p>Content below was written on 26 November 2024.</p>
<h2>back</h2>
<p>It's been a while since my last update. So much has changed since.</p>
<p>I fixed the date formats now, because I noticed I had inconsistenly
used the full and shortened month names on this page.</p>
<p>Today I need to almost force myself to feel free about writing
here - that there's no need to keep it interesting. I can write
exactly what I want - there's no criticism, no feedback, no rewriting.
No offense to you, the reader. I do value your time too. It's only
that this page is very personal to me and I want to keep it as unfiltered,
as original, as I can.</p>
<p>Much has changed in life. I took inspiration from a friend's workout
routine and decided that it was finally time to drop the delay and
get off my ass. I had put up ~10kg in the past 1 year. I was not eating
healthy - both in <em>what</em> and <em>when</em>. I used to feel I had unbeatable
health. I have hit the gym on and off multiple times - each time lasting
more than 3 months. My lifestyle - out of habit and circumstance - had
always involved at least a little bit of moderate working out. That changed
in the past year. To put it poetically - I carried that <em>nostalgia of
being healthy</em> for too long.</p>
<p>That said, the last month has been great for my body. Going back home
for Diwali - surprisingly - actually helped me get healthier. I had
never put it this way before, but now that I have seen and lived
the contrast, my family is actually rather diet conscious. Serving
only half a sweet laddoo is the norm. Growing up in school, my tiffin
box every day was some cooked vegetable and 2 chapatis. No funny treats.
No excess fried snacks. I used to feel sad about it for some time. Now
though, I am extremely grateful to them for that. That was probably
why I felt like I could eat and do whatever and still be reasonably healthy.</p>
<h2>No Gym</h2>
<p>No Gym. Not yet. I have had memberships before and been a regular.
I will join one eventually. For me though, what I really care about
so much more than lifting weights is to be consistent. I am a believer
of persistence. I like woodpeckers. The last time I had a gym membership,
I did not continue beyond 3 months because I was moving. Then I didn't
join a gym because I was once again not sure about how long it'll be
until I move. The time before that, I think I was regular for 6-7 months,
maybe a year. And then I stopped staying healthy when I stopped going
to the gym. A few months after that, I had lost all benefits I gained
from hitting the gym.</p>
<p>This time, I want to stick to a goal that I can do every single day.
That I don't get an excuse out of. For me, that is 10 squats and 10
push-ups. That takes less than 5 minutes, maybe 2. That's what I
set out with around the beginning of this month. It worked out
really well for me because I also started walking 5K steps a day.
A week later, I felt like doing more while I was at it. Skip to
today, I am able to do 3 full sets of
<code>20 squats</code>-&gt;<code>20 lunges</code>-&gt;<code>10 pushups</code>-&gt;<code>20 calf raises / bicep curls</code>-&gt;<code>1 min plank</code>-&gt;<code>30 jumping jacks</code>.
I want to do more and now cut down on break times between these
and properly do what they call <a href="https://www.nerdfitness.com/blog/circuit-training-build-some-muscles-burn-some-fat/">circuit training</a>.</p>
<p>I am seeing benefits already. That + making my diet better is
already making me lose weight and I just feel better.</p>
<hr />
<p>Content below was written on 23 July 2024.</p>
<h2>Ummm</h2>
<p>I haven't written in a very long time. I see it as I see the dates.
I have so much to write about. There have been moments when I have
thought to myself - &quot;yes, this happened. i should put it on the blog
, yes. my internet nerds will like this.&quot;</p>
<p>okay - i won't try to remember everything. I have to be recent.</p>
<h2>Homeserver!</h2>
<p>I think I would consider this the most fun thing I've done this year.
I am off away from the capitalist cloud compute machines that
plague this planet with their unholy subscriptions and have instead
moved to supporting capitalist machine companies that I only need
to pay once. That is only partially true because I still have
subscriptions for other things but not for cloud.</p>
<p>I now host this blog and all of my private software services on
a 5 year old laptop that is, in a utilitarian sense, stripped of
all its components but those that make it a computer. It's like
ship of thesseus but with the components never put back in place
except for those that barely make it a ship.</p>
<p>And yet - it is a mighty beefy machine for a cheap server. It has
20 GB of RAM now (I upgraded) and a 500GB SSD (also, upgraded by me).
This, by cloud machine standards, would have cost me maybe a $100
each month? To me, zero-ing the sunk cost of this old laptop,
was about a $100 in one-time upgrade costs + Rs 300 each month
for a static IP. That's all you need.</p>
<p>It also feels wonderful. I feel like a bit of a sysadmin now.
One of those real ones. I have a private network where I can
host whatever software I want and put exactly the authorizations
and restrictions I need.</p>
<p>Any description of this magnificence other than a dedicated blog
would not be doing it justice.</p>
<h2>Tailscale</h2>
<p>This was an accidental revelation for me as part of the homeserver
setup. It's wonderfully simple to setup. I had an overly complicated
perception of it. And while it is probably extremely complicated
and something that only a subset of 4chan veterans and seasoned
network engineers (or a venn diagram intersection of the two) can
fully comprehend, the user experience for it is so simple a 5
year old kid could do it.</p>
<p>Some setups are painfully difficiult. Tailscale was painfully easy
because it made me curse myself for not doing it before.</p>
<p>Anyways, with this VPN setup, I can enforce access restriction
to only allow this private access to myself. Not only that,
all of my devices - not only this homeserver - can act like
servers with a domain name and IP address. This means I can
setup a private notes server, setup Obsidian + Syncthing,
and SSH from any device into any other without shedding any sweat.</p>
<p>It's so good. I can also always choose to move to a self-hosted
headscale server and get the same benefits. I chose to not do it,
at least right now, because I like the folks at Tailscale. I
trust them more with my private network's security than I do
my (still very good) sysadmin skills.</p>
<h2>Health</h2>
<p>I need to take care of my health. More than I had to earlier.
I realise I'm getting unfit. I used to go to the gym and I
hadn't fully internalised that it has been a <em>very</em> long time
since then. It's time I need to start from zero. Lose weight.
Walk more. Eat better.</p>
<hr />
<p>Content below was written on 13 February 2024.</p>
<h2>Hi Again!</h2>
<p>I'm coming back to writing on this blog after... 4 months now. A lot
has happened. I am very excited. I thought this might be the right
place to start because this has been my favorite part about my website,
in all honesty.</p>
<p>I am happy to have the people I have in my life right now. That set of
people has changed slightly over the last 4 months. My lifestyle, too,
has changed now.</p>
<p>It has been almost 5 months now working at Gooey.AI - and I like it.
I am working on hard problems and I like that I'm good at it. I'm learning
new shit. LLMs, AI deployments, Kubernetes challenges, GPUs, costs, and
more.</p>
<p>I have also been working on <a href="https://github.com/sudha247/ocaml-joy"><code>ocaml-joy</code></a> -
which is rather fun. I, Sudha, and our Outreachy intern Fay, are building
a creative coding library for OCaml - inspired from <a href="https://github.com/fossunited/joy">joy</a>,
the Python original that <a href="https://anandology.com">Anand</a> built at <a href="https://fossunited.org">FOSS United</a>.</p>
<p>There's books and blogs on my reading list that I am so eager to get to.
There are some new ideas I learnt that I can't wait to write about. I guess
micros section would be the right place to put it.</p>
<p>I also went on a trip to Banaras recently - that was so many new India things
that I had not seen earlier. It is so rich with politics and culture and
I couldn't help but notice the social difference that is present there.
It feels unlike any place I have visited earlier - though I have to admit
that I haven't travelled North India enough. I loved their food and I'm glad
I had a friend who was brought up there who showed me around. That was a
beautiful and a rather striking trip for me.</p>
<hr />
<p>Content below was written on 12 October 2023.</p>
<h2>nix and nixOS</h2>
<p>I made a friend at PyCon who was into nixOS, and then few days later --
actually today -- I again met a couple of weirdos who were into nix. I
take it as a sign that I should also try it out. One sentence I remember
from the conversation I had today was &quot;functional is fruity&quot;. I don't
know if I have it in me to be able to forget it anytime soon.</p>
<h2>Gooey.AI</h2>
<p>Kinda big update -- I have joined <a href="https://gooey.ai">gooey.ai</a> as an engineer.
I think my first day was 18 September. It's been almost a month, and I like it
here. I had to choose between Gooey and a couple of other offers, and I was
very confused when I made the decision to join Gooey. It's interesting that
this is another job that I got through my network. In fact, all of the offers
I had this time were through my network.</p>
<p>I was thinking about this -- along with this very interesting book I had been
reading (called Chance by New Scientist) -- that serendipity is weird like
that. You keep making friends here and there and then some day you bump into
them when they are hiring and you're looking for a job. I had met Dev from Gooey
at <a href="https://indiafoss.net">IndiaFOSS</a> last year and then bumped into him at a
meetup recently.</p>
<p>There's a lot more about this book called Chance that I would like to talk about
-- and maybe also talk about why that was even relevant in this context -- but
first let me talk about this new job.</p>
<p>I like it. It's challenging in a couple of ways. First, it is in a fast domain
where I have some context but there's always a lot more to learn. Second, it
is a different kind of company than any I've been in before. It is further
in terms of product-market fit than companies I've worked with before and it
also has different processes. There are things to learn here.</p>
<p>My priority this time, my personal KPI here, is to just get (important) things
done. Something I learnt over the break is that good (senior-ish?) engineers
don't yap about it when they get stuck -- due to let's say, ambiguity, or some
tool not having X feature, or some vendor not offering exactly what was expected
-- no they take that into account and find a reasonable path ahead. That often
takes a lot of domain understanding and communication with stakeholders. Sometimes,
you might be the best person available to take a call on something. In that case,
it might be good to suggest a solution based on your understanding and talking about
it with your stakeholders. Sometimes it could be a compromise on the scope or UX,
or it could just be that you simply need more time. Communicating all of that with
stakeholders boldly is important. That's what I want to get better at. Getting
things done. The rest of it is only a means.</p>
<h2>Taking rest</h2>
<p>A hard truth is that focus is a limited currency. The last time I tried doing too much
of focused activities, I couldn't ship quality stuff on time because I was left
with so little energy. Often this went into a cycle of procrastination and guilt and
terrible routines. I am more cautious about it these days. I do set aside some extra
time besides work to do other interesting things, but I'm taking it super slow these
days. If anything, I would spend maybe another hour or two on a weekday on it. No more
than that. Most other time is spent hanging out with friends or just doing something
fun.</p>
<p>I immediately feel it when I'm out of energy. I once spent more time than I intended
on an open source project I'm working on and I had no energy the next day to do any
meaningful work. So far, this has worked very well for me.</p>
<h2>OCaml</h2>
<p>I've been doing a bit of OCaml again. It's a wonderful language. I, Sudha, and Aryan
are mentoring an Outreachy project in the OCaml org to port
<a href="https://github.com/fossunited/joy">joy</a> from Python to OCaml. I'm excited by the
idea and I feel that I'm learning more about open source as I do this. It is the
<em>contribution period</em> right now -- when all applicants accepted into Outreachy
contribute to different projects. The intern is chosen based on how they did in
the contribution period. For a new project without a lot having been decided on,
it is sometimes a bit overwhelming to need to create new issues on demand, help
everyone trouble shoot different development issues, and keeping up with code
reviews. I've been setting aside a limited amount of time each day for this.</p>
<p>It reminds me of the time I participated in Google Code-in. It must've been
similar or worse for my mentors then. Who had their own things to do and
were volunteering for this -- and then kids like me would keep pushing code,
incessantly ping them for reviews, and ask for new tasks to work on.</p>
<h2>PyCon India</h2>
<p>I attended PyCon India recently in Hyderabad. It was fun. The highlight for me
to be honest was just catching up with old friends, making new friends, and
the discussions I had outside the auditoriums. Lots of interesting chats.
No name dropping here but if I met you at PyCon India, I loved hanging out
with you. The other highlight was that I also caught up with another friend of
mine in Hyderabad. We went around the good cafes, did some gokarting, talked shit,
and just had so much fun.</p>
<p>I namedropped Gooey to folks, showed them awesome <a href="https://gooey.ai/qr-code/">QR codes</a>,
and tried to hire folks (it's hard!).</p>
<hr />
<p>Content below was written on 11 September 2023.</p>
<h2>SolapurFOSS and creative coding</h2>
<p>I spoke at <a href="https://indiafoss.net/solapur/2023">SolapurFOSS</a> on 27th August
about creative coding in Python. This was by far my most OK talk. I was
relaxed, I knew what to say, and I slowed down to read the room throughout
the talk, repeatedly. The slides were also not all filled with boring text
and I feel like brought out some emotion and connected with the audience.
I felt validated when multiple people in the audience then asked me about
some details that I had mentioned at some point in the talk. They were
listening to me.</p>
<p>I filled the slides with a lot of visuals and very litle text, and that
seemed to work well. <a href="https://docs.google.com/presentation/d/1e18tIK_dXVz3LNR6Vq4qjkT1S1DbzBnKAKvHGPdmdY0/edit#slide=id.p">These</a> are the slides,
in case you want to have a look.</p>
<p>This also encouraged me to try and make some sketches in preparation for
the conf. I made <a href="https://mon.school/sketches/1745120">this</a> one sketch
for a waving Indian flag on the moon. It took me some time to figure out
the wavy part but it felt satisfying to do it. That however got too
complicated to explain in an <em>Introduction to Programming</em> kind of talk,
so I made <a href="https://mon.school/sketches/1745537">another one</a>! This one
I was proud of because it actually looked pretty.</p>
<p>I also went back home during this time and looked at a randomly
generated rectangle/line-pattern that <a href="https://rasagy.in">Rasagy</a> was
exhibiting at JSConf as bookmarks and which I had bought from him.
That reminded me of how beautiful this can be and that once in a while
I can do that if I'm feeling artsy..</p>
<p>I also watched <a href="https://www.youtube.com/watch?v=gT9Xu-ctNqI">this talk</a>
by an engineer at Brilliant.org about a tool called Diagrammar. As I
was running into the limits of an SVG backend that mon.school was
using (won't render if too many transformations), I really wanted to
use this but it wasn't open source :(</p>
<p>As context, the prior inspiration for me to explore all this were 3blue1brown's
videos. Such as those on the Mandelbrot set with its beautiful fractals!
I can't have that on mon.school (I tried). But I did learn that the
library with Grant himself created the animation videos with was actually
open source and it is called manim.</p>
<p>I haven't started learning manim yet (that's a lie, i have watched one
youtube explaining its API), but I am very excited to try that out.</p>
<h2>IndiaFOSS</h2>
<p>I'm volunteering on the website team for IndiaFOSS. We had some problems with
the designs and I couldn't give enough time early on in the volunteering season
so we lagged a bit, but my team mates have done a great job and I added the
finishing touches to whatever we had built. It's still lacking some info, but
when that happens we will go live immediately and I (like everyone) will be
happy.</p>
<h2>Summer of Code</h2>
<p>My Google Summer of Code season has come to an end. I liked it. I got some
learning and got to contribute to something that will be used by a lot of
DBAs around the world on a large volume of data (scary!?).</p>
<p>I don't feel too good about doing it though, because I didn't need it.
GSOC is a great way for folks new to FOSS to get involved with open
source projects, and make those first commits easy because you have
a mentor with you. I can manage without this help, and could've even
mentored some participants had I gotten involved with one of the orgs
earlier.
Moreover, the project turned out to be too easy for me. I probably
only spent about 40-50 hours or much less during the actual coding
period to finish off the entire scope of the project, over 3 months.
Of course, this was also because I had done good research about what to
do before I started and I stuck to the initial plan, but still that's
too little effort to run away with it.</p>
<p>Why did I participate in GSOC then? To be very frank, I got scared about
my career and the future opportunities I would have open to me if I didn't
have either a college or something prestigious such as <em>GSOC</em> on my resume.
I did have <a href="https://codein.withgoogle.com">Google Code-in</a>, but I guess people
hardly know about it. It's fair because even those who know about it
might not be able to guess the difficulty or significance of it.</p>
<p>I don't have that fear anymore that I have started applying and
interviewing with companies, but perhaps that's already because of gsoc?
I don't know what the alternate timeline would've looked like, or it was
better or worse.</p>
<hr />
<p>Content below was written on 31 July 2023.</p>
<h2>Bangalore flat</h2>
<p>I have finally moved into this new flat. I'm the new person in a sharing
flat and I like my company. The room is small I think I've put in enough
effort to make it feel homely and I like it. It's actually just about
sufficient for me. I did a small celebration with a couple of friends
and kept a momento for it. I don't know how long it will be with me
and then thrown out. It's a special feeling this, though, having my
own space for the first time. It's not a PG, not a hostel. My own
flat room. I was not expecting it to feel this special but it does.
The beauty of simple things :)</p>
<h2>Reading</h2>
<p>I went to a popular bookstore here today, called Blossoms', and
got a couple of books. I wasn't intending to get any at first
and I only went because of a friend, but then I started reading
something with interest and after reading a few pages I thought
that I should after all buy something. Let's hope it's not
one of those times that I buy something and then never read it again.</p>
<p>I got a different book from the one I had started reading, and I
understand that it sounds like a stupid thing to do, but I had
my reasons. I am too lazy to explain, but I did get a new scientist
book and then I saw one at the counter titled &quot;The Beauty of
Everyday Things&quot; and it attracted me. I want to find beauty
in everyday things. It's my kind of philosophy. That there
is beauty in everyday things. There are things to feel excited
about which aren't out of the ordinary. I want that, so that
I don't trivialize the ordinary.</p>
<p>I wanted to hear out the author's take and explore my own
beliefs more deeply. I read a couple of pages and so far I've not
been disappointed. I am so excited that I'll read a few more
this night.</p>
<p>I'm done filing my ITR after all ehehe.</p>
<p>Life's good. I need a job or a convincing reason to startup is all.
I'm in some talks and experimentation. Might have an update on this
at some point.</p>
<hr />
<p>Content below was written on 14 July 2023.</p>
<h2>Bangalore</h2>
<p>I'm in Bangalore again and I like it here. It feels
homely. I found a place finally in a good location
and in good rent. I took a few days to think about
whether or not to take it but I think I should take
it. Having my own place would let me do more. Like
setup a self-hosting server, have a better home-office
setup and more. It's a small room only and perhaps
it won't be everything I want yet but it is good
enough to get me started.</p>
<h2>Rust</h2>
<p>I've been liking Rust. It's very different from other
programming langauges in terms of its memory ownership
model and I'm probably doing a few things wrong but
I'm getting by with documentation and ChatGPT. I'm
learning to write more and more idiomatic Rust.</p>
<p>I started with the Cryptopals challenge after building
an OS in Rust.</p>
<h2>Cryptopals</h2>
<p>Cryptopals challenge: <a href="https://cryptopals.com">https://cryptopals.com</a>.</p>
<p>I had a look at this after seeing it on some chat group and then
thought that doing it in Rust would be good to learn Rust
and also to do something hard (<a href="https://blog.nateliason.com/p/proof-you-can-do-hard-things">a blog I found on HN about proving you can do hard things</a>).</p>
<hr />
<p>Content below was written on 7 July 2023.</p>
<h2>Too many ORs</h2>
<p>I was reading the below things I wrote. I use way too ORs,
and always get sidetracked by some possibility. Too many
maybes. It shows that I'm okay with not resolving them
into something certain. Agnosticism, when I don't care
enough to resolve that confusion. It makes sense to be
honest. If I didn't know myself better I would have even
said that it's smart.</p>
<h2>Solo trip</h2>
<p>I'm on a solo trip to do a retreat of sorts and complete
<a href="https://os.phil-opp.com">this series</a> about building an
operating system in Rust. I wanted to learn both about
operating systems and to write Rust and this has worked
out really well. I'll consolidate my notes into this blog
tomorrow (actually today, it's midnight right now).</p>
<p>I've learnt a whole lot and I can't really complain about
it. It does feel sort of lonely and I do get bored very
often but I came to some important realisations in the
time that I had to ponder about myself and the world.</p>
<p>I saw this page once again after some time. This is one of
my more honest pages. I feel like this is way more honest
than the about-me page I have written. Of course everything
is factually correct but I always don't know what to say
when asked to tell about myself (like in the about-me page).
It's more of the kind of page where I try to sell myself to
readers by throwing at them every interesting thing I have
done. I should refine it some day haha. Last thing I want to
do is say haha in a written post.</p>
<p>Let's talk about the philosophical realisations though, because
I'm more interested in that than the technical stuff right now.
Not philosophical, but let's say, one of peace and calm.</p>
<p>I felt like I wasn't able to contribute as much as I had wanted
during my time at Pipal Academy. I feel that a bit. In hindsight,
I think I was over-spending my focus and leaving little
room for it to be spent on thinking creatively. What I mean is
that on the same day, I would work on something challenging
at work, then spend a few hours studying something as intensive
as theorem provers or lockfree data structures where I had to
put in a lot of effort to understanding it. I felt like I was
setting out focused time, taking fewer leaves than I was
supposed to, and doing what I had to do to get things done but
things were still going slowly. I wasn't able to have a good
opinion on the product because I didn't leave myself enough
focus to spend on creative thinking like that. I don't know if
that's what is called burnout, but I had normalised something
like that. Now, after taking some break and feeling much more
creative about things, I'm starting to notice it. On days that
I do something focused in the evenings (such as <a href="https://twitter.com/n1kochiko/status/1676312742989537280">building a
snake game</a>)
(in a new language with a strange runtime in which I didn't even
have heap allocation), I have a hard time doing focused things
the next day. I felt like this was the norm for people my age,
who study at college, do more things in evenings, and sleep
late. Maybe it's a bit different or maybe it just doesn't work
for me, but I need my breaks. I have decided to take note of
it in the future as well, and say no when asked to work on
something and I don't want to spend any more focus on that day.</p>
<p>I feel like work could've gone better if I had learnt it
earlier. Even Anand told me that if I am excited about something
I should take a day or two off and just get done with it.
It makes a lot of sense now and I can understand why. Or at
least why <em>I</em> should do that.</p>
<p>An adjacent thought was that I should also think about what
it takes for me to be satisfied, in the long run, and take
interest in it. I feel like I've tried too hard to do things
to prove my worth, maybe even to myself. I don't have to do it,
or I think I have done enough. I must stay true to myself and
think of things that genuinely make me happy. This website
for example, shouldn't be about me flashing my tech skills
(like I do in the about page), but about things that get me
excited. Of course, at certain times, it doesn't have to be
told and I'll only write about things that do get me excited.
That still doesn't mean that it was my intention when I came
across it. Or maybe whatever I do has already been what gets
me excited and what I genuinely do like. In any case, what I
do in the future, or write about, should come from a good place.
As in, it shouldn't be because I'm trying to establish something
about myself. That happens naturally and it's not up to me.
It is like in writing, when the focus should be on clarity and
strength and style follows.</p>
<p>I've been thinking about life too, and what philosophical idea
of it I feel home with. I don't know what's the right way to
approach this. Maybe you catalogue through many philosophies
and then pick one or something, but it doesn't matter. I have
long believed that life is meaningless and whatever anyone
does has no extra meaning other than to us. But then in that
meaninglessness, one should make up meaning and be true to it.
As an example, all relationships end some day, ultimately because of
the the transientness of life itself. They are also void of any
profound meaning because there will come a day when no one will be
around to appreciate it. But that doesn't mean they aren't to be
experienced. Life too is meaningless and transient, but it can
be beautiful. That is because it is subjective. The things that make
a life beautiful too are often transient. If they aren't experienced
then all we are left with is dullness. I don't care much for dullness.
I know I sound too philosophical and dreamy, like a drunk poet,
but in that moment of thought, it all just felt right. Like
I had figured out life. Of course, 30 year olds will be quick to
jump at me to point out my naiveness, but then what can you do.
You think, you write, you get critiqued and judged.</p>
<p>It's a good principle. It makes me live to stand up to my opinions
and take chances. Because if I don't know what it's like to live
the way I feel I should live, then what else is there to live for?</p>
<p>I think I might be becoming way too honest on this now section.
It's like a journal entry, quite literally. To be very honest, I
had tried to maintain a journal in the past and I have never been
as good writing there as I am here. Maybe there's a certain
refined quality that comes out due to something here.</p>
<h2>Rust OS</h2>
<p>I really don't feel like going much into detail about this. I will
consolidate my notes tomorrow anyway and write a bit about what
this was like. Let me say a few things though. When I look back
at my notes at the end of a day, I realise that I learnt a heck
of a lot. The knowledge gap between the start of my day and the
end is so noticeable. I had never observed this before. It's a
principle I should keep in mind that you can learn a lot in a
single day if you do it right.</p>
<p>I recently looked at Partick Collison's blog titled &quot;<em>Fast</em>&quot;.
Things that were built very fast. I was amazed that the v1
of Unix was built in 3 weeks by Ken Thompson. That Linus
only took a few days to write Git and was already hosting it
by day 4 or something. I'm quite amazed.</p>
<h2>CFP for PyCon India</h2>
<p>I thought a bit about the CFP for PyCon India. I had a
conversation with an experienced community member about
topics for a talk proposal and he had suggested the topic
of things like best practices that are actually useful
to a large audience. I thought about it, broke it down,
and came up with a rough outline
(https://memos.kaustubh.page/m/190). I titled it &quot;Writing
idiomatic Python in 2023 and ahead&quot;. When I thought about
it, I actually felt that it could help a lot of people.
match-case will catch on, but then there are things like
asyncio and typing which are often not used in the best
way. asyncio because the API isn't that intuitive, and
it's easy to not take full benefit of it. For example,
people will <code>await</code> coroutines in a loop instead of
creating background tasks where they can be executed
concurrently. I myself have fallen for it when I started
and it should be more widely known. <code>typing</code> because
it is moving so fast and we have new patterns that are
lesser known. Even things such as not needing to import
annotations for <code>List</code> and <code>Dict</code> are not as widely known.
Now we are getting template types too.</p>
<hr />
<p>Content below was updated on 27 June 2023.</p>
<h2>Reflecting</h2>
<p>These past few days I've mostly not been doing anything work related,
except for one consulting gig that I had taken up that kept getting
stretched. Finally, I was able to work around some problems I faced
and come to something that was acceptable for the client. I'm happy
about it because it started out as something that was very
technically challenging and a lot of parts that were new to me.
I kept running into different problems, and limitations of tools,
but finally I worked around things and got it done. Now my deep
knowledge of how the internet works or how pip install works came
to good use.</p>
<p>For example, we were using an open source tool for a use case that
we didn't want to spend engineering effort on (too niche and
complex) and it was giving us some problems with its limitations.
It was running as a reverse proxy and we wanted to run some script
on its page without modifying its content. It wasn't very
extensible by default. We needed to run some javascript on that
page, and there was no reasonable way to change the output of its
HTML without changing its inner code. What I did was added an
Nginx rule for one of its javascript assets, and made that also
include the script that we wanted to run.</p>
<p>The other was about knowing how pip install works and how the
modules are stored. Then I got what <code>--target</code> option was and
was able to fix a few issues properly because of that.</p>
<p>Of course, linux and bash scripting as always helps a lot. When
working with other people, it helps that you can write scripts
for them to run rather than explain something step-by-step.</p>
<p>That was on my mind on some of the days, but besides that I am
reflecting. I want to make a new section for the next part.</p>
<h2>Ownership and usership</h2>
<p>I've been thinking about what I had been working on at Pipal
Academy. It's a neat tool to build courses. When I was working
on it full-time, I would try to think about a user and how they
would like it to be but I think that my standards then fell short.
Now that I think about the same software as FOSS software that I
would use for something myself, I am much much more opinionated
and I want it to be at a much higher standard and I would in fact
be ready to contribute that to the project.</p>
<p>It's strange how a simple change of perspective like this changes
how effective I can be at building something. I think this
distance from the product, this lack of a feeling of ownership,
brought out more effective ownership from me. It reminds me of
one of the chefs from the anime &quot;Food Wars&quot;. I can't remember
the Japanese name of it now, but it's one of the few animes that
I've watched and liked. There is one character in it, one of the
seniors of the protagonist, and one of the elite 10. Maybe the
<em>first seat</em> of the 10 elite chefs at this super elite culinary
school. He treats his ingredients with finesse and builds his
dishes with elegance, and yet all the emphasis is on perfection
as if it is something objective.</p>
<p>I feel that distance brings out more objectivity and is important
to build a good project.</p>
<hr />
<p>Content below was updated on 19 June 2023.</p>
<h2>Applying again</h2>
<p>I started applying to companies and looking at ones that do interest me. I got a quick callback
from X big tech consulting company and I got rejected because I don't have a degree and background
check or travelling outside India would be a problem. I don't really buy that line of reasoning.
I mean, larger companies than them have people from India working without a degree, and they
don't have a problem. But it is what it is.</p>
<p>Now I am looking at a few other companies and starting up as my options. I don't want to be in
a purely consulting role because I want to be in a team and learn from others, hopefully
not in a remote setting.</p>
<p>I know that I can get away with it, and that I have a bottom line of consulting or working at a
startup that will sustain me, but I can't say the same for everyone in tech who would skip college.
Moreover, I don't want &quot;sustenance&quot; to be what people who skip college (or me) aim or settle for.
Maybe there is actually scope for career services for talented techies without a degree. I don't
know how much business sense it makes, but it surely would change lives for the better. And like I
had mentioned on the now page before (i hope so?), more people would feel comfortable not going for
a traditional degree if options like this existed. I feel a certain conviction about it but
I'm not sure of its business potential.</p>
<p>Maybe it's actually worth building a startup around it. I am taking a break as well. This might be
a good thing to do. At least it will do something about my curiosity to know whether something like
this has business potential. No, I don't want to do it like bootcamps and suck out of salaries from
my own people. I'm not even sure if a direct hiring model is the best way to go about it. If I am
to take money from companies instead of people, what can I provide them that will make them want to
pay for it?</p>
<p>I'll think more about it from different perspectives and with different assumptions. I like what I'm
thinking and I want to do this.</p>
<hr />
<p>Content below was updated on 16 June.</p>
<h2>Thinking about what to do next</h2>
<p>I am back at home in Latur, from Bangalore. I've been relaxing since my exams last week.
Winding down and letting go of all the stress from work and exams. Now I'm thinking about
what to do next. One thing I know for sure is that I want to work at the edge of my
abilities. I want to do something challenging. Ideally, I want it to be something
that is technically challenging, but I am also okay with starting up because I know
that I'll get the challenges anyway. Moreover, I think there's a lot to be learnt
there in terms of interpersonal skills and the potential upside is huge.</p>
<h2>Google Summer of Code</h2>
<p>Back to GSOC after a break last week. Writing this parser is tricky at times. We didn't
have a catchup call this week yet. I'm trying to complete this one thing but I feel stuck.
I should communicate yes.</p>
<hr />
<p>Content below was updated on 9 June 2023.</p>
<h2>Leaving Pipal Academy</h2>
<p>Today is technically my last day at Pipal Academy. I'm slightly emotional about it
because I had a great time working here and I loved learning from Anand.</p>
<p>Technically it's midnight and 9 June is behind me. But okay.</p>
<p>Lambda Retreat was one of the best things that I have attended and it got me into
programming languages.</p>
<h2>JSConf</h2>
<p>JSConf was okay. I enjoyed Rasagy's session on making art with code, and even
made my own version of it on <a href="">mon.school</a> with python. I liked that, and meeting
friends, enjoying <a href="https://twitter.com/DudeWhoCode">Naren</a>'s coffee, and talking
about FOSS United there. But besides that, I did not particularly like it. It felt
a bit weird that speakers were treated very different. All were put up at five star
hotels (not me) with special dinners and arrangements made. Someone I randomly met
asked me &quot;What qualifies you to be a speaker?&quot;. Maybe I didn't understand their question
correctly, but it pissed me off. Of course, I was polite and decided to answer the
better question &quot;What qualifies you to speak about the FOSS United community?&quot;, but then
I was thinking about it for some time after that. You don't have to be high gods to speak
about something, or get validated by working for one of the big tech companies. It made me
sick to think that it might be the baseline to have a voice. If something is interesting
(and being presented nicely, which comes with practice), I don't care who is delivering it.
Grr.</p>
<p>I don't know, maybe it was that I didn't gel that well with the community or that I
was too tired or something, but I felt like I had been to better conferences.</p>
<h2>Feeling like an impostor</h2>
<p>I was feeling like an impostor. Just now. About an hour ago. Then I started writing this,
and I remembered while writing about JSConf that I had made a
<a href="https://mon.school/sketches/1738158">sketch for a name</a> generator inspired by
<a href="https://rasagy.in">Rasagy</a>'s talk, then I felt like wanting to tweet about it so I made
a few changes and I started feeling confident again. There is something wonderful about
being able to do this. I like it. My routine has changed so much I stopped doing things
like this and then feel bad for myself.</p>
<p>In any case, I do remember what I was thinking when I was feeling like an impostor, so I'll
go ahead and write about it anyway. I build a lot of cool projects, but then they either
have very little scope or they don't do much. At Pipal Academy, I wasn't able to contribute
as much as I would've liked to in the span that I was there. I also wasn't living up to
my expectations in terms of productivity and speed. This was a bit of a surprise to me
but I couldn't fix it even though I tried. It felt weird because a lot of people I meet
tell me that I'm smart. I know that I take time to understand things deeply and ask
questions when I am interested and there's more to take from a conversation. I build a good
model of things in my head, but then what if my problem is that I can't deliver on it?
In my head, I think of it as the <em>immature guru problem</em>, when you have knowledge that you
never put into use, but tell that to others. Spineless gyaan. I've been this immature guru
when I was a child and was just good enough to make my school cricket team but not good
enough to be an important player. I still had opinions and lots of useless trivia that I
would just tell other people. Sometimes it worked, sometimes it didn't. But I don't want
to be that person. Programming is the one place I feel confident enough to be an elite
player and I want to do that.</p>
<p>I feel like I have a bias towards action when I get excited about something, which is
not that infrequent. I made the sketch thing right after I saw Rasagy's talk in half
an hour or so.</p>
<hr />
<p>Content below was updated on 21 May 2023</p>
<h2>Reflecting on the &quot;Now&quot; Page</h2>
<p>This experiment of adding a <em>Now</em> page is actually going quite well. It's a
place for me to put things that aren't significant enough to blog about,
or what I'm just thinking about and haven't done anything about yet.
I think the main point of this is that it makes me think about <em>What I'm
doing Now</em>, and that often leads from one point to another to another,
which I wouldn't have thought or written about otherwise.</p>
<p>An RC alum, who happened to hang around in the same communities as me, also
saw my tweet and reached out on Telegram. I had seen this person on the
FOSS United Telegram group before but because I hadn't met them in person,
I didn't know much about them. That was definitely interesting and I'm happy
that they reached out after seeing this.</p>
<p>One more person I met at the FOSS United meetup (yesterday - 20 May 2023),
very randomly, mentioned that I appeared on their timeline and they knew
me from my posts after I told what my name was. That was new and felt
really nice. We got talking and this person also knew someone I've been
good friends with. Small world?</p>
<p>I think I should thank <a href="https://twitter.com/sadhlife">Tushar</a> because
probably both of them saw my tweets because of him (for one person, I
know for sure, the other one I suspect). The serendipity of the internet,
twitter, and communities. Even when twitter is not in a great state.</p>
<h2>Travelling to Bangalore</h2>
<p>I travelled to Bangalore the day after my last update, checked in at a hostel
where I've previously stayed. I want to find a place but I haven't been
satisfied with anything yet, and the one that I was eager to take I didn't
get because the dude didn't vacate on agreed-upon date. We had a handshake
deal and the owner was okay with me moving in to his room, but I should have
made it a bit more formal and make him commit the same to the owner as well.
It might also just have been a good thing because to be fair, I've been more
productive back at my home away from the city, and this gives me a bit more
time to decide how I want to live. I can also come visit hostels when I feel
like I need to get out.</p>
<p>Bangalore has been fun though. Fun, yes. Not productive enough yet. I'm doing
some GSOC thing that I have been putting ahead for about a week. The coding
period starts on the 29th and I want to stick to my commitments. But I want
to catch up with people and go to meetups now that I'm here, that's part of
why I come to Bangalore. I met some of the hostel regulars with whom I've become
friends from my previous visits, caught up with
<a href="https://twitter.com/@cmeghashyam">Megh</a> and <a href="https://twitter.com/@Param108">Param</a>,
attended the <a href="https://forum.fossunited.org/t/bengaluru-foss-may-2023-meetup/1934">FOSS Meetup</a>,
and then had a farewell night for a friend who was leaving Bangalore.</p>
<p>I could go on in more detail about each, because each one of these interactions lasted
a while and was interesting. Maybe that'll go to my <a href="/micros">microblog</a>.</p>
<h2>Running into self-hosting problems</h2>
<p>This was the first time I can remember that I have ever run into some self-hosting
problem that came as a surprise to me. <a href="https://github.com/miniflux/v2">Miniflux</a>,
which usually runs without problems, suddenly stopped working when I tried to add
someone's RSS feed to it. I was doing some experiments to setup a working two-way
email on my droplet and ran some scripts directly for that, and then uninstalled
it with some pre-made scripts. I suspected that this must've broken the database.
The authentication was working and I made sure that the database was there too.
The problem was that one of the scripts had changed the authentication method from
peer (the one I used) to password for all users except the default &quot;postgres&quot; user.
My experience with <code>pg_hba.conf</code> came in handy and all was good within an hour.</p>
<h2>Setting up self-hosted email</h2>
<p>I tried to setup self-hosted email. Setting up incoming mail was straightforward.
I had done it before too. This time I looked at Sivers' post on tech independence
and decided to try out Mutt. Mutt looks good. I like that you can use shortcuts
to do things quickly, but getting used to the shortcuts will take some time. The
other thing is that actions immediately happen when I hit keys in Mutt. I prefer
Vim in that sense because if I accidentally press some keys it either doesn't
do anything significant in the default normal mode, or it is undoable. I'll give
it some time though. Setting up outgiong mail was not at all straightforward though
and I realised that there's really no way around Google's restrictions intended
to avoid spam.</p>
<p>As I was writing this, I realised that I hadn't dived deep enough into self-hosting
email and that I should try a little more. I'm doing that now, I learnt about PTR
records and DKIM until now and it's interesting.</p>
<p>It's the next day now. Actually it's 2:30 AM on the 23rd, the original thing was written
on the 21st. I was doing DKIM but did not complete it yet. I'm waiting for the CI tests
to run on capstone. I can do this in the meanwhile.</p>
<hr />
<p>Content below was updated on 17 May 2023.</p>
<h2>Working on Capstone</h2>
<p>Capstone is moving ahead. We have a demo planned. My sleep routine
is still bad, but I feel more relaxed doing work now.</p>
<h2>Applying to Recurse Center</h2>
<p>I applied to Recurse Center last week. I've been anxiously checking
my email since then. I'm very excited about it. Since I read their
about page, I feel like it's <em>the place</em> for me to be at. Recently
I've also been thinking about my career choice to not go for a
college. I wonder if there would be more poeple not going through
college if there were career services and strong communities with
smart people who were as ambitious and willing to learn as me.
Something that will make sure you don't miss out on opportunities
by not going to an MIT or a Stanford or an IIT. I have a strong
intuition that the answer is yes. Lots of kids must have thought
about it but then not gone for it because of the uncertainty.
Lots of kids wouldn't even think about it because they've worked
to get into a good college for years and they won't consider
another option. If there were good career support for
kids like that, who are smart but still don't go to college,
we would see a lot more people not going for the traditional route.
There are good reasons to not go for the traditional route. I'm
proud of what I've done. I don't think I would've thrived as much
had I been in a college, even a top tier 1 college.</p>
<p>I think I might present about this at Barcamp this weekend. I'm
not sure. In any case, I think a good first step is to curate
resources and make it known that my contact is open for anyone
interested in not doing the traditional route. I'm not much of a
big shot myself, but I'm sure I can help younger people like me
in some way.</p>
<h2>Learning Rust</h2>
<p>I finally picked up Rust. I like it. Borrowing is clever. I like
the guarantees I get from it. It's neat. I love the pattern
matching, range, and mutability being separated from referencing.
It feels like a better version of C and OCaml, with some sugarings
of Python. I don't take it lightly when I say that it's better
than OCaml. I still see some of the same patterns. One cool thing
that not many people might notice is that a Rust function with
multiple lines must have semicolons on all lines but the last one
to indicate that the function goes on. This is like OCaml. Straight
out of it.</p>
<h2>Writing a tutorial on Interactive Theorem Prover</h2>
<p>I got the idea today morning, to write a detailed post on
interactive theorem provers (with Coq), such that it is much
more approachable to normal programmers. I call it &quot;Interactive
Thereom Prover for Normies&quot; for now, and I have some content
for a draft. I like the informal lingo I've put in there. I think
it might help programmers, or at least it will help me better my
writing or my Coq skills.</p>
<h2>Raytracer in a weekend?</h2>
<p>I saw this in a post by Julia Evans, in her DNS server in a weekend
post. I had already built my own DNS server from scratch a while
ago, but I liked the other posts that she mentioned to, and one
of them was this Raytracer in a weekend. I ignored this then but
then I came across this again while looking at Carol's blog on
<a href="https://kipp.ly/blog/past-webdev/">projects beyond webdev</a>. Carol
is a Google Code-in Winner like me, and while I haven't been in
touch with her that much, I have read some of her blogs and admire
what she does. I also learnt that she, like me, skipped college.
I did not know this. I also came across her one other post on
<a href="https://kipp.ly/blog/no-not-good-enough/">skipping college</a>, and
I love it. I relate so much to this. I don't think I would've
thrived as much in a college, even a top tier one. I suspect
that being able to build things with programming whenever I learn
something is actually what keeps me from getting bored of it. It
is so relatable that I think we could be friends (👉👈). I should
tell her that I liked her blog and that I too skipped college.</p>
<p>I wonder if she has been to recurse center.</p>
<h2>Meeting old friends</h2>
<p>I'm in my hometown and many of my school buddies are home too.
We are meeting today and I am quite excited. I feel like I haven't
seen some of these people in years. Maybe we had met less than a
year ago. I want to know what they've been up to and how they're
doing.</p>
]]></description>
      <pubDate>2024-02-13T04:40:00+05:30</pubDate>
    </item>
    <item>
      <title>Google Summer of Code 2023 at Postgres</title>
      <link>/posts/google-summer-of-code-2023-at-postgres/</link>
      <guid>/posts/google-summer-of-code-2023-at-postgres/</guid>
      <description><![CDATA[<blockquote>
<h3>For easy access</h3>
<ul>
<li>GitHub Repo: <a href="https://github.com/the4thdoctor/pg_chameleon">the4thdoctor/pg_chameleon</a>
</li>
<li>GitHub Diff: <a href="https://github.com/the4thdoctor/pg_chameleon/compare/50ed941...bdc376a">50ed941...bdc376a</a>
</li>
<li>Branch with my changes: <a href="https://github.com/the4thdoctor/pg_chameleon/tree/gsoc-2023">gsoc-2023</a> (This can be installed and run manually)
</li>
<li>Changes after GSOC: <a href="https://github.com/the4thdoctor/pg_chameleon/pull/167">Pull Request</a>
</li>
</ul>
</blockquote>
<p>This post is to mark the completion of my participation in Google Summer of Code
2023 with the PostgreSQL organisation. I worked on a database replication
tool called <a href="https://github.com/the4thdoctor/pg_chameleon">pg_chameleon</a>, to
rewrite the SQL parsing library. My mentors were
<a href="https://github.com/the4thdoctor">Federico</a> -- the original author of this
library --, and <a href="https://github.com/andreasscherbaum">Andreas</a> -- a PostgreSQL
contributor.</p>
<p>Previously, this library was built with regex that was hard to understand and
consequently to modify. It had to be made simpler so that new contributions could
be added without so much difficulty. I did most of my research into this before
and during the community bonding period, talked to Federico -- my project mentor
and the library maintainer -- about a solution, and we settled on replacing the
regular expressions with a Python library called
<a href="https://github.com/python-parsy/parsy">parsy</a>.</p>
<p>The first half went smoothly, but then life got in the way in the second half,
for both the mentors and me and we couldn't make much progress. We discussed the
scope of what was left to be done after the initial goal was reached in the first
half at the earliest date we could, but I couldn't make time for it later. ~~I'm
relatively free now and I can make time for the rest. My goal is to add all of that
in the next couple of weeks.~~ I made a pull request with those changes in the week
after the official completion of GSOC.</p>
<p>The first part was to port all existing parser functionality to parsy. This was the
brunt of the project and where we were solving the pressing problem of too much
complexity. This got completed on, or in fact slightly before the time we had
allocated for these changes. It got easier because of the research and prototyping
we did early on -- in the commmunity bonding period and the first week. This is
the diff for the changes I made during the first half of GSoC:
<a href="https://github.com/the4thdoctor/pg_chameleon/compare/50ed941...bdc376a">50ed941...bdc376a</a>.</p>
<p>I started with adding a parser for the <code>CREATE TABLE</code> statement in parsy and
replacing the regular expressions with this. This was the first prototype because we
wanted some additional validation that this tool is powerful enough to meet our
purpose of achieving simplicity while getting things done for us.</p>
<p>I also had to make some changes to how the tool is packaged, because it was written
when Python 3.5 was still supported. That is no longer the case and we decided to
support 3.7 and up, because that was the oldest Python version compatible with
parsy.</p>
<p>Later on, I added more changes to convert the rest of the regular expressions to
also use parsy instead. This was a big change and I felt a relief when it was done
and working correctly. I was a bit cautious with my changes, so I also added some
automated tests with <code>pytest</code> to make sure that there was no regression. The reason
regression was possible was because with parsy, the order in which we combine the
sub parsers also does matter. If we combine sub-parsers with the <code>OR</code> combinator
(<code>|</code> or <code>alt(...)</code> in parsy) for example, it will parse the statement with the
earliest sub-parser that matches successfully. This big change converted a lot of
Python
if-else / find-replace logic to declarative parsers. It also made each parser more
complete and accurate, because now we could match with the entire SQL statement
rather than
checking for patterns here and there, and extracting smaller patterns step by step.
For example, the different subcommands (<code>ADD COLUMN</code>, <code>DROP COLUMN</code>, <code>ADD INDEX</code>, ...) that are available after the <code>ALTER TABLE</code>. Having separate end-to-end parsers
for these subcommands helps because that makes it easier for us to add parsers for
other subcommands (e.g. for adding / altering index that I'll get to later on).</p>
<p>Alter table and create table statements benefitted the most from this rewriting
because they are so complex and there be so many different operations that they
can entail. Also because SQL is very lenient and there are many different ways
to do the same thing. Previously, they were handled with multiple regular
expressions and functions with if-else chains. For example, the alter table
statement was being handled by over 100 lines of function code, complex regular
expressions, and naive find-and-replace for normalization. This could for example
break when strings inside the statement contained whitespace. There were also some
redundant find-replaces that were hard to spot because of the complexity. All this
could now be split into declarative parser chunks that were easy to read on their
own, and could be combined in simple albeit powerful ways to give us the complete
parser. To complete the example, this is the code of the final parser of the
alter table statement:</p>
<pre><code class="language-python">alter_table_statement = seq(
    command=seq(ci_string(&quot;ALTER&quot;), whitespace, ci_string(&quot;TABLE&quot;)).result(&quot;ALTER TABLE&quot;),
    __space=whitespace,
    __schema=seq(identifier, string(&quot;.&quot;)).optional(),
    name=identifier,
    alter_cmd=optional_space_around(
        alt(
            alter_table_ignored,
            alter_table_drop,
            alter_table_add,
            alter_table_change,
            alter_table_modify,
        )
    ).sep_by(string(&quot;,&quot;)).map(lambda ls: [x for x in ls if x]),  # remove none values
)
</code></pre>
<p>Each of <code>alter_table_ignored</code>, <code>alter_table_drop</code>, and so on, were their own little
parsers that were easy to read through. The result of parsing a valid statement is
a Python <code>dict</code> with the provided kwargs, with the keys having the <code>__</code> prefix
getting conveniently discarded. Even though there is some convention and prior
knowledge required to understand it, the result is something much simpler and
readable at a reasonable cost for the abstractions we chose.</p>
<p>The second part, which was to add support for some newer commands, is yet to be
done. I'll be doing that now in the next couple of weeks.
This includes adding support for parsing the <code>CREATE INDEX</code> statement and variants
of it in the <code>ALTER TABLE</code> form. We will also be adding support for parsing a
functional index so that it can be logged and alerted to the user. The same goes
for other special case indices that should be manually handled such as fulltext and
spatial indices. Another case to be handled is partial indices and the type of the
index used.</p>
<p>Adding parsing capabilities for all of this is much easier and simpler because of
the changes we made earlier. Regular expressions would need a lot of fore thought,
verification, and probably automated tests to make sure that they capture the
input correctly. All of this is very tedious and the resulting code is far from
obvious to a reader. In comparison, the statements written with parsy can be
understood only with familiarity of a few primitives and basic operators.</p>
<p><strong>2 Sep 2023 Update</strong> I created a pull request with the changes from part 2 and
they are now being reviewed. <a href="https://github.com/the4thdoctor/pg_chameleon/pull/167">Pull Request</a>.
I liked the idea of composability here, as compared to regular expressions. Here while
building parser for <code>ALTER TABLE ... ADD INDEX</code> statements, I could re-use the
index definition parser I wrote for <code>CREATE TABLE</code>. Then when I added features
for parsing a bigger set of index definitions, those readily started working with
<code>CREATE TABLE</code> too. This way we can re-use features but also keep parts of our
code consistent. The trade-off is that bugs could pop up at other locations because
the behaviour of their utilities is changing. I think a good next step would be
to add more tests for the parser so that we can be sure of it for all the cases.</p>
<p><strong>5 Sep 2023 Update</strong> I have finally, successfully completed Google Summer of Code! 🎉</p>
]]></description>
      <pubDate>2023-08-28T00:14:16+05:30</pubDate>
      <category>tech</category>
      <category>python</category>
    </item>
    <item>
      <title>Syntactics and Semantics</title>
      <link>/posts/syntactic-and-semantic-equivalence/</link>
      <guid>/posts/syntactic-and-semantic-equivalence/</guid>
      <description><![CDATA[<p>I don't usually do purely mathematical posts, but this topic really fascinated me and I want to share it.</p>
<p>If you think about it, math is formal language we created to help us describe the world. It is extremely
rigorous and in the end, and very importantly, it seems to make sense to us.</p>
<p>In the early 1900s, Bertrand Russell worked on Principia Mathematica, a set of rigorous proofs of what we
consider fundamental truths in math. For example, their proof for <code>1 + 1 = 2</code> was 360 pages long! This is
way too rigorous for something that we would consider obvious. But this now is the perfect segue for
me to introduce the terms syntactics and semantics.</p>
<p><em>Semantics</em> is anything that deals in meanings -- such as interpretations or observations -- as opposed to
deriving one truth from another purely through mathematical rules. The opposite, deriving new truths from
established ones using rigorously checked math rules, would be to deal in the <em>syntatics</em>.</p>
<p>This here highlights the contrast between what we find obvious and what Russell devoted 360 pages for. It
may be obvious to us -- semantically -- that <em>1 + 1 = 2</em>, but to a mathematician, a pressing question is
whether that truth can be arrived at by taking some even more fundamental truth and using mathematical
rules to transform that -- that is, syntactically.</p>
<p>We should want the semantics and syntactics to agree with each other. If we are able to disprove <code>1 + 1 = 2</code>
syntactically, it would mean that our mathematical rules are broken and that possibly some other propositions
we thought to be true are not. We also want to be able to conclude decisively, only with fundamental math
truths that <code>1 + 1 = 2</code>. If we cannot, it means that our language is not powerful enough to model the real
world.</p>
<p>These properties of a system are called <em>soundness</em> and <em>completeness</em> respectively. We want math to be both
sound and complete. We want to be able to only prove true statements, and we want to able to prove all true
statements.</p>
<p>A related idea is one of syntactic equivalence and semantic equivalence. Two statements are syntactically
equivalent if we can reach either one from the other using only mathematical rules and not interpreting
the statements at any point. Parallelly, if two statements are semantically equivalent, we can reach either
one from the other by interpreting their meanings.</p>
<p>We want semantic equivalence to entail syntactic equivalence (completeness) and vice versa (soundness). Even
though they are different ideas, they can be confusing because in a good syntactic system such as math, both
do happen together. Math feels like it is complete and sound.</p>
<p>I just said that math feels like it is complete and sound, but mathematicians are crazy and they've proved that
it isn't complete and it can't prove its own soundness. I first heard about these ideas in this Veritasium video.
As smart as mathematicians are, I feel like they created a footgun for themselves with this.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/HeQX2HjkcNo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
<p>In any case, math isn't just hocus pocus. It gives us so many interesting things, such as the idea of syntactics
and semantics. Knowing the distinction between the two gives us words to describe systems and reason
about their correctness. The system in question need not always be math. It could be a programming language, a
state machine, computer hardware, etc.</p>
]]></description>
      <pubDate>2023-08-11T02:29:51+05:30</pubDate>
      <category>math</category>
      <category>tech</category>
    </item>
    <item>
      <title>Netflix Scoop, Politics, and Journalism</title>
      <link>/micros/netflix-scoop/</link>
      <guid>/micros/netflix-scoop/</guid>
      <description><![CDATA[<p>I watched Netflix's new series called Scoop and finished it all within a couple of days. It was so engaging.
It breaks my heart to watch it. I know that this kind of thing happens. Politics. Journalism. Ambitions.
Scapegoats. I'm really thinking about ethics. To think of what humans are capable of, it leaves me
pessimistic about us as a society. I don't know if there's ever a solution to this. Politics and
power games exist in every society large enough to be called a society. One person, motivated by their
own ambition or survival, would not look twice at the consequences of their actions. Doesn't matter
whether it's a capitalist economy or some other kind, there will always be this power game and there
will always be losers.</p>
<p>It makes me think about the consequences of my own work. I'm new and young, and I don't think much of it
when I <em>do my job</em>, but probably some day my actions too will have consequences for people. I hope I am
ready for it when that day comes and that I am aware enough to see it. My actions have consequences even
now, just that they aren't serious enough for me to ponder over like an ethical dilemma. Either that or
I'm too dumb to see it. I'll keep an eye out though.</p>
<p>It makes me think about journalism too. I respect them, but I don't think I'd ever want to be in such a
position. There are winners and losers in the game of politics, and I don't want to play it for the
fear of being a loser. The stakes are too high if you lose. I wonder if this system can be made fair.
I would love to believe in it but my pessimism gets in the way. After all, it's justified given all
that we humans are capable of.</p>
]]></description>
      <pubDate>2023-07-01T02:49:20+05:30</pubDate>
    </item>
    <item>
      <title>Todo List on a Blockchain</title>
      <link>/micros/todo-list-on-a-blockchain/</link>
      <guid>/micros/todo-list-on-a-blockchain/</guid>
      <description><![CDATA[<p>This is uncommon, but while building my personal life management app, I thought of
building a todo list app on blockchain, where each todo is a block that cannot
be deleted. You have to do it.</p>
<p>I don't know how it helps. I guess it doesn't. Not at all, really. But then I thought
of it and it was one of those &quot;aha, that actually isn't half bad&quot; moments that was
actually very very bad.</p>
<p>But in any case, with everything that is being built in the blockchain world right now,
I'm sure this use case easily beats a lot of ventures.</p>
]]></description>
      <pubDate>2023-06-29T01:39:02+05:30</pubDate>
    </item>
    <item>
      <title>Too Many Threads</title>
      <link>/micros/starting-too-many-things/</link>
      <guid>/micros/starting-too-many-things/</guid>
      <description><![CDATA[<p>Threads of things/works/todos.</p>
<p>I started with learning about Coq through the Logical Foundations book and I have done about 4-5
chapters from it. After that I got busy and it is still at that point. I also started learning
Rust and I haven't moved ahead much in the past few days on the Rust OS book.</p>
<p>I felt like there would be more but these are the only two that I have started and not kept going
with. I was tempted to start a few more things but did not because I had this in mind. I should
learn how to keep removing things out of that todo bucket.</p>
<p>There are some more things that I should do and do quickly, but I haven't gotten around to. Maybe
I should make an app for me to keep track of all this. Would this be another thing that I build
halfway? Maybe, I'll bite anyway.</p>
<p>I'd much rather like a phone app and a desktop app, but I would need to learn how to build either,
so I'll instead start with a web app. I'll start with an API and then a simple frontend in Svelte.
That should do well.</p>
<p>I hope I get this done tonight.</p>
<p>For now, I am thinking that it should have a todo list (or todo bucket?) and a journal for sure.
Rest can come later.</p>
<p>Fingers crossed.</p>
]]></description>
      <pubDate>2023-06-29T01:30:47+05:30</pubDate>
    </item>
    <item>
      <title>A Tiring Week</title>
      <link>/micros/a-tiring-week/</link>
      <guid>/micros/a-tiring-week/</guid>
      <description><![CDATA[<p>This week was <em>extremely</em> tiring. I have been finishing up on whatever has been on my plate at work. Besides that,
I had taken up a consulting gig for a friend and that turned out to be much more work than I had first imagined.
I had a lightning talk this Friday at JSConf, and I had to complete my Google Summer of Code work as well. This was
the first week of GSoC and my mentors seemed to be quite happy with the work that I had done. I didn't find as much
time to work more on it as I had promised on the weekend but I'm sure that I can cover up on it. This week was
extraordinarily tiring, but I have done most of my work for the consulting gig, my mentors were happy about the
work that I had put in at GSoC, and I did do most of the work that was on my plate at Pipal Academy. I wish I never
have a week like this again. It was extremely tiring and full of context switching between different things. I should
be more cautious before taking up some work and actually enquire further about it. It was my fault for overcommitting,
but I'm happy that I'm coming out on top and that I am in fact completing my work on time with a high standard of
quality.</p>
<p>I've had trouble understanding burnout before. Because there were times when I was working too much and thought I had
burnt out but I didn't feel it that much. Sometimes, even when I hadn't worked as much, there would just be a few days
when I felt zero motivation to do any work and it wasn't that I had done too much before. I think it's not the quantity
of work that causes burnout, but the stress or anxiety of not getting your work done on time. I had never before felt
this close to burning out and I'm sure I would be if I had to go on with it for another week or more. I like programming
and solving problems, but only when I am excited to do it and not because I'm taking money for it.</p>
<p>This is an important realisation for me, and an important consideration before I decide what I do next. I think a break
lasting a few months where I do something challenging would be what I need. I'm also considering starting up because that
would take me back into this area where I have to do work because I take money, but I'll learn how to deal with it.
It's weird that I am considering it but I don't feel comfortable having this weakness which would be problematic
for me because I would inevitably get in situations where I am in tight deadlines. At least then it would be for me
and I might feel more comfortable doing it.</p>
]]></description>
      <pubDate>2023-06-04T20:49:53+05:30</pubDate>
    </item>
    <item>
      <title>Too Much on My Plate</title>
      <link>/micros/too-much-on-my-plate/</link>
      <guid>/micros/too-much-on-my-plate/</guid>
      <description><![CDATA[<p>It is decided that I will be leaving Pipal Academy, after a year of working there. I should've made another post
for it, and I will do it, but right now there is too much on my plate.</p>
<p>I have a conference talk coming up tomorrow and I'll be preparing for it from 3pm onwards today. I didn't work on
Capstone yesterday because I met with my parents and then had to work on the TML project. I have a GSOC catch-up
coming up tomorrow, the first one after the start of the coding period and I haven't done anything about it yet.
It's not that I'm not working, but I'm not able to give enough focus to all of them.</p>
<p>I also have a term 2 examination for my online college next week and I haven't done anything at all. I feel very
bad about this. I'm sure I'll be able to catch up in a few weeks, but I shouldn't have been in this position
in the first place.</p>
<p>Too much on my plate, spreading myself too thin, wearing too many hats.</p>
<p>I missed a Ted Lasso episode on a Wednesday for the first time in Season 3.</p>
]]></description>
      <pubDate>2023-06-01T13:19:02+05:30</pubDate>
    </item>
    <item>
      <title>Fasting</title>
      <link>/micros/fasting/</link>
      <guid>/micros/fasting/</guid>
      <description><![CDATA[<p>I got interested in the idea of fasting after a conversation
and wanted to try it out.
I decided to do a 24-hour fast after the lunch I had
yesterday. That's a good enough start and won't overwhelm
me.</p>
]]></description>
      <pubDate>2023-05-29T11:14:11+05:30</pubDate>
    </item>
    <item>
      <title>Its Actually Not That Long</title>
      <link>/micros/its-actually-not-that-long/</link>
      <guid>/micros/its-actually-not-that-long/</guid>
      <description><![CDATA[<p>I was worried that my last post was too long. It's actually not. Still small enough.</p>
<p><em>How's this for a micro post, past myself?</em></p>
]]></description>
      <pubDate>2023-05-24T05:32:28+05:30</pubDate>
    </item>
    <item>
      <title>Rubberducking</title>
      <link>/micros/rubberducking/</link>
      <guid>/micros/rubberducking/</guid>
      <description><![CDATA[<p>I tried rubberducking today to fix a bug.</p>
<p>It has felt a bit weird to me before although I have done the same
thing in some other form, like while pair programming and code
review.</p>
<p>This was the first time that I remember consciously practicing
rubber ducking. And it worked very well. I didn't even
complete one paragraph and I figured out what was wrong.</p>
<p>This was the whole thing that I wrote:</p>
<blockquote>
<p>I've run into this weird bug which started when there was some change with the rendering. Initially it failed because there was some 500 error when project couldn't be fetched. Now it errors because the create user project page gives 200, but then the user project doesn't actually exist.
Ah, now that I say it like this, the solution is obvious. It was probably because the code for the view was changed. Of course. The logic to create the user project was removed completely.</p>
</blockquote>
<p>A lot of context is missing and I was just thinking out loud, but it
helped me arrive at the solution.</p>
<p>The interesting thing I think is that I had already spent about half an hour
to one hour looking at the logs and trying out different things to fix this
issue but writing about it made the problem immediately clear. My intuitive
thinking is bad at such things and I should remember that explicitly thinking
about things like this works better.</p>
]]></description>
      <pubDate>2023-05-23T02:35:49+05:30</pubDate>
    </item>
    <item>
      <title>Working From the Train</title>
      <link>/micros/working-from-the-train/</link>
      <guid>/micros/working-from-the-train/</guid>
      <description><![CDATA[<p>I am in the train right now, about half an hour to go until Bangalore. The interesting thing about this journey was that
I did my work while sitting in the train. It was a side upper seat in a 2AC. I had wanted one of the larger seats in
the bigger part of the compartment but the side upper has its own perks. The group-of-four seats (or group-of-six in 3AC
coaches) put you snap in front of another person. If it's a family (as my kind neighours were), there is very little
privacy you have to yourself. The last time I was in such a group seat, I was surrounded by a family with luggage
and kids. I interacted a bit with them but my awkwardness at small talk and the langauge barrier meant that it wasn't
too much fun. I did meet a cute girl across from me on the same trip and small talked a bit, but she seemed as awkward
as me if not even more, and we spoke online for a bit after the journey. But it's not every time that it happens and I
like this quiet space. This train also has a light bulb and charging point dedicated to my seat! I was about to run
out of battery twice on my phone and once on my laptop and I could just put it to charge.</p>
<p>But while all of that is OK, the more interesting thing is that I did work from a train. The internet connectivity has
gotten a lot better in remote areas. I had to SSH into a machine to be able to work. Yes it dropped often, but I could
live with it. In some places, I was also getting 5G internet. This is so good to see. Besides, I also tried to do
some learning which didn't need internet. I completed some more of the Logical Foundations book I am referring to.
The neovim interface is lovely. I also learnt some more Rust. The more I read about it, the more similarities I see
to OCaml. It feels somehow more appraochable though. I wonder what can be done in OCaml to make it as approachable.
I am also doing this.</p>
<p>Back to the train journey because I'm finding it very interesting and more things keep coming up. I can't see the window
from here. That's a downside. I also spilled some tea on myself because I wasn't seated and with a side table like the
grouped seats have. The curtains, yes. I spoke about privacy but didn't mention curtains. There were curtains. One
person woke me up from sleep unintentionally by opening the curtains to find their seat. I was startled and angry because
the numbers weren't up anyways, but I was too sleepy to say anything. Then one more person tried to open the curtain a bit
at night while I was awake, but it was only someone from the other side trying to get down. I don't know why I remembered
these things, they seem very insignificant. It's a bunch of sentences from me, but I guess a skilled writer or a Jerry
Seinfeld could be able to make something creative out of it. Maybe I should give it to ChatGPT to see if it make something
creative out of this seemingly random collection of sentences.</p>
<p>I did give it to ChatGPT. I asked it to write about the journey in the style of Jerry Seinfeld. Make some observational
humour. It wasn't bad what it came up with :</p>
<blockquote>
<p>Imagine me stepping onto the stage, waving a hand, grinning. “Hey folks! How are you all doing today? You know, I recently took a train ride to Bangalore. Yeah, you heard it right, a train ride. Not just a ride, I tried working while on it. Not making chai, but coding in Rust and OCaml. But let's not get into that, it's scarier than ghosts for some people in here.&quot;</p>
<p>Laughter fills the room.</p>
<p>&quot;So, I booked a 'side upper seat'. Sounds fancy, doesn’t it? It’s like the train's way of saying 'Congratulations! You're alone but elevated.' It’s an introvert's paradise, I tell ya! Sure, I could have gotten a group-of-four or six seat, but nothing quite beats the intimacy of a solo seat. In the group seat, you've got so much company, it's like a mini family reunion where you're the odd in-law nobody likes.”</p>
<p>Giggles ripple through the audience.</p>
<p>&quot;And here's the fun part: you know how they have these light bulbs and charging points dedicated to your seat? I was half expecting a mini-fridge and a foot massager next. Train rides have evolved, folks! I mean, soon they’ll be asking if you want a hot towel and an in-flight meal!”</p>
<p>The audience chuckles.</p>
<p>&quot;But the surprises didn’t stop there, no sir! The internet connectivity, guys! I had better reception in some remote field than I do in my own living room. I was coding, learning, even got 5G! That’s faster than my neighbor's Ferrari, and he lives in the city!&quot;</p>
<p>The laughter builds.</p>
<p>&quot;However, everything wasn’t as smooth as my internet connection. You see, the side upper seat lacks a few, let's say, comforts. Ever tried having tea without a table? It's like playing hot potato with yourself, only the potato is a hot cup of tea. And the loser is your shirt.”</p>
<p>I pause for the laughs.</p>
<p>&quot;But the best part was the 'curtain surprise'. Oh yes! The curtains. I was sleeping like a baby, dreaming about my code compiling without errors, when suddenly, BOOM! Curtain slides open! I swear, I jumped so high, I could have switched places with the guy in the upper berth! And the perpetrator? Just another passenger, fumbling in the dark. I tell you, it's funnier than a horror movie!&quot;</p>
<p>The room erupts in laughter.</p>
<p>&quot;In all, folks, the journey was like coding in OCaml, unexpected, surprising, at times frustrating, but never boring! I hope you enjoyed my little train escapade.&quot;</p>
<p>And as the laughter fades, I walk off the stage with a grin, leaving the room still echoing with chuckles and applause.</p>
</blockquote>
<p>And just like that, it's no longer so much of a micro post. That's fine, let me sleep and dream about my code compiling without errors.</p>
]]></description>
      <pubDate>2023-05-19T06:52:30+05:30</pubDate>
    </item>
    <item>
      <title>Completing One Year at Pipal Academy</title>
      <link>/micros/completing-one-year-at-pipal-academy/</link>
      <guid>/micros/completing-one-year-at-pipal-academy/</guid>
      <description><![CDATA[<p>So this happened on 15 May. I'm writing this post on the 17th. How did I know that it was 15th? I went
all the way back in my email inbox to the first one and I got the Google's &quot;Let's get you started&quot;
email on May 15 2022. I had taken a 2 week break soon after, as I remember, to find a flat in Pune.
That was in the first two weeks of June. Now that I think about it, what was I doing taking two
whole weeks off to find a flat, and <em>in Pune</em>. I haven't done that for Bangalore and I get by.</p>
<p>I was aware that I was nearing the completion of a year but I wasn't sure whether I had started in May
or June. I had a conversation with Anand about this and what's ahead. Let me put that all in this blog.</p>
<p>It won't be much of a <em>micro</em> blog then. Based. I'll do it somewhere else and keep it to myself for the
moment.</p>
<p>OK but let me say a few things anyway. It's the first time I've ever completed more than a year besides
Hyperlog. Well, Hyperlog. That's for another day. I've grown a lot as an engineer. I finally feel
more confident socially, like I fit in. It took me one whole year at Pipal, and &gt;5 years since I started
using real languages for building things, for that. I feel like I'm a slow mover socially. Or maybe something else
that I don't know. I don't get <em>free</em> (as in freedom) with people until I know them for a very long time
and even then I sometimes struggle, but at least the intent is clear in those situations. I think I'm
getting better at that but I don't understand it one bit.</p>
<p>For now, let's go Kaustubh. Long live you.</p>
]]></description>
      <pubDate>2023-05-17T09:25:24+05:30</pubDate>
    </item>
    <item>
      <title>Starting a Microblog on My Blog</title>
      <link>/micros/starting-a-microblog-on-my-blog/</link>
      <guid>/micros/starting-a-microblog-on-my-blog/</guid>
      <description><![CDATA[<p>A few days ago I came across <a href="https://twitter.com/swaroopch">Swaroop CH</a>'s Twitter profile and started looking at his blog.
It had entries from 20 years ago and more. I loved it. The communities that he was part of then are the communities that
I am a part of now, and I could even recognize some of the names that he mentioned. His blog was split up into lots of small
posts, similar to what people these days would use Twitter for. He had blogged about some of the early meetups that he went
to, including the forming of <a href="https://www.swaroopch.com/first-bangalore-python-meetup/">BangPypers</a>, early versions of
<a href="https://www.swaroopch.com/barcamp-bangalore-6-day-1/">Barcamp Bangalore</a>, and other important career events for him. I loved
it and thought to myself that I want to have something like this 20 years later too. I use Twitter a bit too much, but whatever
I put there is eventually lost. I want to maintain this list as a slightly more sanitised collection of important
career and personal events for me, that isn't as smeared by rubbish as my Twitter timeline. I also worry about Twitter shutting
down. haha. 2023 joke. kids, there used to be something called Twitter...</p>
]]></description>
      <pubDate>2023-05-17T08:38:51+05:30</pubDate>
    </item>
    <item>
      <title>The End of Web UIs?</title>
      <link>/micros/end-of-web-development/</link>
      <guid>/micros/end-of-web-development/</guid>
      <description><![CDATA[<p>I got access to ChatGPT's plugins features a couple of days ago. I tried using some of them. Diagram It felt very useful, where I could
ask ChatGPT to explain some idea to me with diagrams. I also tried WebPilot which could go through links. I wasn't too impressed by the
other ones I tried, but it was intriguing to see in the list plugins for companies that could directly provide services from the
chat interface (such as restaurant reservation, house brokering, fashion shopping, domain registration, etc.). I could see that it's
not such a bad UI for end users. They can type (speak?) in natural language and get the results they want without navigating through
complex UIs. This reminds of Google Actions (and corresponding APIs in Alexa, Siri, ...) where the interface for users is to speak
something to take some action rather than navigate through UIs. ChatGPT is much better at interpreting what the user meant. I had
implemented a Google Action back in 2018-2019, which was my first decent hobby project. It was a blindfold chess app and I had made some
clever hacks in parsing the user's text that allowed it to pretend that it was smart and could understand moves said in any one of the
many different ways. But it was a challenge to implement it, and it wasn't perfect. With things like ChatGPT, we can directly interpret
what someone meant and encode it into stricter forms. I can see how it can be a very good interface for doing things smartly.</p>
<p>This makes me wonder whether it is the end of web UIs (for mass consumers). It isn't too difficult to hook up a speech-to-text to ChatGPT,
and then use plugins to get our services across. Plugins can do visualization, provide links, and (I anticipate) other richer ways to
interact in chat such as choice buttons and structured forms. Sure there are security problems and jailbreaking hasn't been solved yet,
but it hasn't even been a whole year since ChatGPT was released. When we do find robust ways around it, would the web economy become
redundant and switch to plugins? I can already see how specialised plugin developers would be helpful to a lot of businesses. I think I
might learn it too.</p>
<p>In any case, the web will surely look a lot different in a few years. Maybe people will use ChatGPT-esque applications instead of web
browsers. Maybe there will be bodies similar to IETF that make standards for APIs that ChatGPT and company must follow for their plugins.
Maybe there's something even beyond plugins.</p>
]]></description>
      <pubDate>2023-05-15T06:51:46+05:30</pubDate>
    </item>
    <item>
      <title>On Sleep Schedule</title>
      <link>/personal/sleep-schedule/</link>
      <guid>/personal/sleep-schedule/</guid>
      <description><![CDATA[<p>My sleep pattern has become very idiosyncratic recently, and not by my own volition. I can't seem to sleep more than 2-3 hours at night in one go, and then I sleep for hours in the day.
Tonight I was on Reddit till 1.30-2am, and then woke up at 4.30. I've been up since then and its still morning. I have also gotten into munching on sugary things when I'm up at night, but
I'm not too worried about that because it's fairly limited and I know I can control it. I should. I haven't been going to a gym because I don't have a permanent geographical place where I can
get a gym membership. Maybe exercising will help with my bad routine. I started out by staying up till 3-4 am to work, then till 6-7 am. Some days, I would get very little sleep because
I still had commitments in the day. Then last week for the second half, I felt very burnt out and didn't have the energy to do any work. That's when this started. I would sometimes play games
at night or watch Netflix, anything that wouldn't make me use my brains. It was weird, that period, I felt a strange headache and I was disinterested in everything. It does surprise me that
it suddenly changed during the weekend and I even wrote a project happily (gitignore.sh) and then learnt some Coq as well. Granted I was still quite distracted, but this is much better than
last week.</p>
<p>Maybe I'll fix my sleep schedule, but that's not on the top of my priorities. I know, I complain that this is a problem, but then I'm not willing to do anything about it. That's because maybe
it's not as bad. I still get time to do things I like and work. I don't hate it not having to be all sweaty from the summer heat. I'll take it if my schedule fixes itself when I travel, but
I don't think I will explicitly act against it.</p>
]]></description>
      <pubDate>2023-05-15T06:40:50+05:30</pubDate>
    </item>
    <item>
      <title>Interpretation is a vehicle of human comprehension</title>
      <link>/micros/there-is-no-true-interpretation-of-anything/</link>
      <guid>/micros/there-is-no-true-interpretation-of-anything/</guid>
      <description><![CDATA[<blockquote>
<p><em>There is no true interpretation of anything; interpretation is a vehicle of human comprehension. The value of interpretation is in enabling others to fruitfully think about an idea.</em></p>
<p>- Andreas Buja</p>
</blockquote>
<p>I'm quoting this from the book &quot;The Elements of Statistical Learning.&quot;</p>
<p>This sounds fascinating, and seems prescient now that we are in the
age of ChatGPT and LLMs, huge statistical models that interpret
things for us.</p>
]]></description>
      <pubDate>2023-05-14T07:46:45+05:30</pubDate>
    </item>
    <item>
      <title>How to Setup Coq in Vim</title>
      <link>/posts/how-to-setup-coq-in-vim/</link>
      <guid>/posts/how-to-setup-coq-in-vim/</guid>
      <description><![CDATA[<p><em>Using Coqtail to setup the Coq interactive theorem prover in Vim or Neovim</em></p>
<p>Besides using Coq in the Coqide or with Proof General in Emacs,
you can also set it up on Vim/Neovim with the <a href="https://github.com/whonore/Coqtail">Coqtail</a>
extension.
I had some difficulties while setting it up and there weren't
that many resources for it, so I'm documenting the steps here.</p>
<p>We'll have to:</p>
<ol>
<li>Set up Vim / Neovim with Python support
</li>
<li>Install coq for command line
</li>
<li>Add the coqtail plugin / setup colors or colorscheme
</li>
</ol>
<h2>1. Setting up Vim / Neovim with Python</h2>
<h3>On Vim</h3>
<p>If you use Vim, you will have to compile it yourself with
the <code>--enable-python3interp</code> flag. I haven't tried this myself
because I use Neovim, but this seems to be the way. I found out
about the flag from <a href="https://stackoverflow.com/a/69937353">this stackoverflow answer</a>.</p>
<h3>On Neovim</h3>
<p>On Neovim, it is much easier. You must have the <code>pynvim</code> package
installed on your system's Python3.</p>
<pre><code>$ python3 -m pip install pynvim
</code></pre>
<p>Once done, check that the Python interpreter setup in your
Neovim is same as the one for which you installed <code>pynvim</code>.
You can check that in Neovim by running <code>:checkhealth provider</code>.
Make sure that the <code>python3_host_prog</code> mentioned in the logs
is the same as the interpreter where you installed <code>pynvim</code> (
<code>$ which python3</code>). If not you can set
<code>let g:python3_host_prog = &quot;/path/to/python3&quot;</code> in your <code>init.vim</code>
or <code>init.lua</code>.</p>
<h2>2. Install coq for the command line</h2>
<p>If you install directly from the website, you'll get CoqIDE and
an interactive command line program. But you need the <code>coqc</code>
command to be there in $PATH for coqtail to use. You can set
that up by installing Coq with a package manager.</p>
<p>For me it was brew.</p>
<pre><code>$ brew install coq
</code></pre>
<p>And then check that <code>coqc</code> works.</p>
<pre><code>$ coqc --version
</code></pre>
<h2>3. Install whonore/coqtail plugin</h2>
<p>I use <code>vim-plug</code> with an <code>init.vim</code>, so for me it was adding
this line to <code>init.vim</code>:</p>
<pre><code>Plug 'whonore/Coqtail'
</code></pre>
<p>and running <code>nvim +PlugInstall</code>.</p>
<p>You can find instructions for other plugin managers in
<a href="https://github.com/whonore/Coqtail#readme">coqtail repository's README</a>.</p>
<p><code>coqtail</code> should now be setup. Open any Coq file (<code>.v</code> extension),
try entering <code>&lt;leader&gt; cc</code>. This should start Coqtail in a
split view. These are some shortcuts that I found useful:</p>
<p>| Shortcut | Description |
| -------- | ----------- |
| <code>&lt;leader&gt;cc</code> | Start coq |
| <code>&lt;leader&gt;cj</code> | Go ahead by one step |
| <code>&lt;leader&gt;ck</code> | Go back by one step |
| <code>&lt;leader&gt;cl</code> | Run all steps until the current line |</p>
<p>One more thing: if you use dark mode, Coqtail's highlighting
color probably won't
match well with the background. I simply switched to light mode
when using Coq (relax, only for Coq). But, if you want to put in
some more effort, you can set that up and it is mentioned in the
repository's readme <a href="https://github.com/whonore/Coqtail#syntax-highlighting-and-indentation">here</a>.</p>
]]></description>
      <pubDate>2023-05-10T04:33:19+05:30</pubDate>
      <category>ocaml</category>
      <category>rocq</category>
      <category>tech</category>
    </item>
    <item>
      <title>Taking ChatGPT&apos;s help in building (relatively) obscure data structures</title>
      <link>/posts/building-a-concurrent-lockfree-data-structure-with-chatgpt/</link>
      <guid>/posts/building-a-concurrent-lockfree-data-structure-with-chatgpt/</guid>
      <description><![CDATA[<p>ChatGPT can help you get non trivial things done. In this case,
I wanted to build something that didn't have much of a precedent that ChatGPT could build on.
However, ChatGPT does seem to have some logical abilities (by generating logical
looking code). I used it as a second brain and dumped some responsibilities on it
so that I could keep my head relatively clear. When building something as non-trivial
as a lockfree data structure, there are many edge cases to consider. I went through
a process where I pointed out cases to ChatGPT, sometimes suggesting opinionated fixes
and giving it outright patches, and sometimes asking ChatGPT to point out these cases
itself.</p>
<p>I think it helped because I didn't have to consider all the cases myself. To go into
some specifics of how lockfree structures are implemented, at the core of it is the
<a href="https://en.wikipedia.org/wiki/Compare-and-swap">compare-and-swap</a> instruction. It
takes a pointer, an expected current value, and a new value, and sets the value at
location pointer to <em>new value</em> only if it is equal to the <em>current value</em> before the
update is done. The whole operation is completed atomically, using a special hardware
instruction that performs both the compare-and-swap in one CPU instruction.</p>
<p>When building a concurrent data structure, we must take into account how different
interleavings of the data structure's methods affect the correctness of our program.
This is complicated. Seemingly trivial things such as computing the size of a
data structure are not trivial, and accepted size algorithms that are shipped
with libraries could have a negative return value with a particular interleaving
with other methods.</p>
<p>That is exactly the problem that my PR focused on. It took me 5-8 hours of work to
do that. The big range is because I was mid-travel and did it in short sprints. Now
that I mention it, that is another advantage of programming with ChatGPT. I would have
otherwise forgotten my chain of thought after a gap, but I had it both in my chats
and queryable with English language :). I had read through the mentioned
paper before though, and I think reading the paper and implementing it in one go
would've taken longer. This is the <a href="https://github.com/ocaml-multicore/lockfree/pull/60">pull request</a>.</p>
<p>I used the GPT-4 model. I don't think the stock ChatGPT model would've been useful in
this case.</p>
]]></description>
      <pubDate>2023-05-05T23:00:00+05:30</pubDate>
      <category>tech</category>
      <category>ocaml</category>
    </item>
    <item>
      <title>Concurrency in a Single Thread</title>
      <link>/posts/concurrency-in-a-single-thread/</link>
      <guid>/posts/concurrency-in-a-single-thread/</guid>
      <description><![CDATA[<blockquote>
<p>Note: An <code>mdbook</code> version, organized into chapters, can be found <a href="https://chennaifoss.kaustubh.page">here</a>.
Its content is licensed under CC (Creative Commons), and if you want to modify it to fit it for your own
use, you can use the <a href="https://github.com/nikochiko/build-your-own-asyncio">GitHub repository</a>.</p>
</blockquote>
<h2>Outline</h2>
<ul>
<li>Concurrency in a single thread
</li>
<li>Generators
</li>
<li>Coroutines
</li>
<li>Scheduling coroutines
</li>
</ul>
<hr />
<h2>Concurrency in a single thread</h2>
<p>Traditionally, concurrency has been a responsibility handed down to the operating system.
A single process can create multiple threads and the OS will run these threads across
its (multiple) cores as it sees fit. It will interrupt a running thread, store its state,
and load another previously interrupted thread for the computer to work on.</p>
<p>It's often not worth doing this. Moreover, writing correct multi-threaded programs is quite
hard as data races and deadlocks are easy to miss. We can solve this problem by adding
another level of indirection.</p>
<hr />
<h2>Generators</h2>
<p>Python has generators that can be created with the <code>yield</code> statement. They are commonly
used to build iterators of some sequence.</p>
<pre><code class="language-python3">def abc_generator():
    yield &quot;a&quot;
    yield &quot;b&quot;
    yield &quot;c&quot;

abc = abc_generator()
for letter in abc:
    print(letter)
</code></pre>
<p>Output:</p>
<pre><code>a
b
c
</code></pre>
<p>You can also use generators to make infinite sequences, which you can stop when you want.</p>
<pre><code class="language-python3">def natural_numbers_generator():
    i = 1
    while True:
        yield i
        i = i + 1

natural_numbers = natural_numbers_generator()
print(next(natural_numbers))
print(next(natural_numbers))
print(next(natural_numbers))
</code></pre>
<p>Output:</p>
<pre><code>1
2
3
</code></pre>
<p>You can similarly also build a Fibonacci sequence with this.</p>
<pre><code class="language-python3">def fib():
    a, b = 0, 1
    while 1:
        yield b
        a, b = b, a + b

# sequence: 1, 1, 2, 3, 5, ...
</code></pre>
<blockquote>
<p><a href="https://anandology.com">@anandology</a> took infinite sequences one step further, adding specialised operations
for them, and making Python evaluate lazily. The result is something very interesting, more like Haskell than
Python. It is out of scope of this blog, but you can check it out in this
<a href="https://github.com/anandology/python-magic-show#fun-with-infinite-sequences">GitHub repository</a>.</p>
</blockquote>
<hr />
<h2>Coroutines</h2>
<p>Coroutines are functions that can be paused and resumed. Generators do just that.</p>
<blockquote>
<p><code>yield</code> keyword is called so because the function <em>yields</em> control of the CPU. This control is given back to
the caller of the generator.</p>
</blockquote>
<p>All Python generators have a <code>.send()</code> method. In addition to continuing the generator like <code>next()</code> does, this
method accepts an argument that is sent to the generator. This value can be accessed as the returned value
of an <code>yield</code> statement.</p>
<p>We can build a worker that computes the square of its input.</p>
<pre><code>def square(x):
    return x * x

def get_worker():
    x = yield
    while True:
        x = yield square(x)

def main():
    worker = get_worker()
    worker.send(None)  # &quot;prime&quot; the coroutine
    tasks = [1, 2, 3, 4, 5]
    for task in tasks:
        result = worker.send(task)
        print(f&quot;square({task}) = {result}&quot;)

main()
</code></pre>
<p>Output</p>
<pre><code>square(1) = 1
square(2) = 4
square(3) = 9
square(4) = 16
square(5) = 25
</code></pre>
<blockquote>
<h3>Aside: A real-world example</h3>
<p>I used this pattern recently to build a ChatGPT chatbot that takes prompts from my terminal.</p>
<pre><code class="language-python">import openai

# configure openai
openai.organization = ...
openai.api_key = ...

def get_chatgpt():
    messages = []
    user_input = yield
    while True:
        messages.append({&quot;role&quot;: &quot;user&quot;, &quot;message&quot;: user_input})
        response = openai.ChatCompletion.create(messages=messages)
        chatgpt_message = response[&quot;choices&quot;][0][&quot;message&quot;]  # chatgpt api detail
        messages.append({&quot;role&quot;: &quot;assistant&quot;, &quot;message&quot;: chatgpt_message})
        user_input = yield chatgpt_message

def main():
    chatgpt = get_chatgpt()
    chatgpt.send(None)

    while True:
        # user_input need not come from &quot;input()&quot; only.
        # for example, it could be over a websocket connection
        user_input = input(&quot;Prompt:&quot;)
        result = chatgpt.send(user_input)
        print(&quot;ChatGPT says:&quot;, result)
</code></pre>
</blockquote>
<h3>Coroutines with return values</h3>
<p>Coroutines (and generators) can return values too. This value can be accessed with
the <code>.value</code> attribute of the <code>StopIteration</code> exception that is raised when the
coroutine completes (the function returns).</p>
<p>We have two methods that translate a number to either its English word or its Tamil
word. There is <code>do_io_work()</code> at the start where this coroutine yields.</p>
<pre><code class="language-python3">def number_to_tamil(x):
    yield do_io_work()
    match x:
        case 1: return &quot;onnu&quot;
        case 2: return &quot;rendu&quot;
        case 3: return &quot;moonu&quot;
        case _: raise ValueError(&quot;x must be in [1,3]&quot;)
</code></pre>
<p>We can get the return value of each like this:</p>
<pre><code class="language-python3">tamil = number_to_tamil(1)
tamil.send(None)  # &quot;prime&quot; the coroutine

try:
    while True:
        tamil.send(None)
except StopIteration as e:
    print(&quot;1 in Tamil is&quot;, e.value)
</code></pre>
<p>Output:</p>
<pre><code>1 in Tamil is onnu
</code></pre>
<h3>Running two coroutines concurrently</h3>
<p>Let's spice up <code>do_io_work()</code> a bit. Instead of letting it be a normal function, we'll
have it be a generator itself, which <code>yield</code>s a random number of times. This will give
code the uncertainty of real-life I/O operations.</p>
<pre><code class="language-python3">import random

def do_io_work():
    # yield a random number of times
    for i in range(random.randint(1, 5)):
        yield
</code></pre>
<p>Let's use this with our <code>number_to_tamil</code> coroutine, along with the <code>number_to_english</code>
coroutine.</p>
<pre><code class="language-python3">def number_to_tamil(x):
    yield from do_io_work()
    match x:
        case 1: return &quot;onnu&quot;
        case 2: return &quot;rendu&quot;
        case 3: return &quot;moonu&quot;
        case _: raise ValueError(&quot;x must be in [1,3]&quot;)

def number_to_english(x):
    yield from do_io_work()
    match x:
        case 1: return &quot;one&quot;
        case 2: return &quot;two&quot;
        case 3: return &quot;three&quot;
        case _: raise ValueError(&quot;x must be in [1,3]&quot;)
</code></pre>
<p>I used <code>yield from</code>, which takes a coroutine, then propagates all the
yields from it to the caller.</p>
<blockquote>
<p><code>yield from coro</code> is the same as <code>for x in coro: yield x</code></p>
</blockquote>
<p>We'll hook all three up in our <code>main()</code> function, to <code>print</code> the English
and Tamil translations of numbers from 1 to 3.</p>
<pre><code>def main():
    for x in range(1, 4):
        tamil_coro, english_coro = number_to_tamil(x), number_to_english(x)
        tamil_word, english_word = None, None
        while True:
            if tamil_word is not None and english_word is not None:
                break

            if tamil_word is None:
                try:
                    tamil_coro.send(None)
                except StopIteration as e:
                    tamil_word = e.value
                    print(f&quot;{x} in Tamil is {tamil_word}&quot;)

            if english_word is None:
                try:
                    english_coro.send(None)
                except StopIteration as e:
                    english_word = e.value
                    print(f&quot;{x} in English is {english_word}&quot;)

main()
</code></pre>
<p>Output (could have a different order):</p>
<pre><code>1 in English is one
1 in Tamil is onnu
2 in Tamil is rendu
2 in English is two
3 in Tamil is moonu
3 in English is three
</code></pre>
<p>Notice the different order of English/Tamil translations in each iteration. We just ran two coroutines
asynchronously.</p>
<hr />
<blockquote>
<h2>Aside: Compatibility with asyncio</h2>
<p>The code that we wrote is not a metaphor for what asyncio does. It is exactly what how asyncio works.
We need to only wrap our coroutines in an <code>asyncio.coroutine</code> decorator to make it work with <code>async/await</code>.</p>
<pre><code>import asyncio

@asyncio.coroutine
def number_to_tamil(x):
    ...

@asyncio.coroutine
def number_to_english(x):
    ...

async def main():
    for x in range(1, 4):
        tamil_word = await number_to_tamil(x)
        english_word = await number_to_english(x)

        print(f&quot;{x} in Tamil is {tamil_word}&quot;)
        print(f&quot;{x} in English is {english_word}&quot;)

asyncio.run(main())
</code></pre>
<p>Output:</p>
<pre><code>1 in Tamil is onnu
1 in English is one
2 in Tamil is rendu
2 in English is two
3 in Tamil is moonu
3 in English is three
</code></pre>
<p>This is still sequential, because we are waiting for <code>number_to_tamil(x)</code> to return
before we start executing <code>number_to_english(x)</code>. We can change that by converting
each coroutine to a task.</p>
<p>While the <code>await coro</code> statement will add coro to the queue and also yield, <code>asyncio.create_task(coro)</code>
will only add the coroutine as a task to the queue, without yielding control. That's why we can keep
adding more tasks until we call an <code>await</code> on something.</p>
<p>But to see this in action, we will need to modify our code slightly to <code>print</code> values as soon as they are
computed. Let's add this logic in a new coroutine we'll call <code>translate</code>.</p>
<pre><code class="language-python3">import asyncio
import random

...

async def translate(language, number):
    match language:
        case &quot;English&quot;:
            result = await number_to_english(number)
        case &quot;Tamil&quot;:
            result = await number_to_tamil(number)
        case _:
            raise ValueError(f&quot;Language {language} is not supported&quot;)

    print(f&quot;{number} in {language} is {result}&quot;)
</code></pre>
<p>Now let's modify our <code>main()</code> coroutine to use this.</p>
<pre><code class="language-python">async def main():
    for x in range(1, 4):
        tamil_task = asyncio.create_task(translate(&quot;Tamil&quot;, x))
        english_task = asyncio.create_task(translate(&quot;English&quot;, x))

        # wait for both tasks to finish
        await tamil_task
        await english_task

asyncio.run(main())
</code></pre>
<p>Output</p>
<pre><code>1 in Tamil is onnu
1 in English is one
2 in English is two
2 in Tamil is rendu
3 in English is three
3 in Tamil is moonu
</code></pre>
</blockquote>
<hr />
<h2>Scheduling coroutines</h2>
<p>We saw a pattern when we were running the two coroutines concurrently.
We tried running the first one, tried running the second one, and returned
only when both had finished running.</p>
<p>We can setup a scheduler and put in a queue to run tasks one by one.</p>
<pre><code class="language-python3">import queue

class Scheduler:
    def __init__(self):
        self.tasks = queue.Queue()

    def add_task(self, coro):
        self.tasks.push(coro)

    def run(self):
        while not queue.Empty():
            task = self.tasks.pop()
            try:
                task.send(None)
            except StopIteration:
                pass
            else:
                # task didn't raise StopIteration, push it back to queue
                self.tasks.pop(task)
</code></pre>
<p>Notice that we aren't storing the returned value anywhere. We'll come to it later.
To test this out now, we will have to <code>print</code> the results of the <code>number_to_tamil</code>
and <code>number_to_english</code> coroutines as soon as they are returned.</p>
<p>We'll create a new <code>translate</code> function to do this.</p>
<pre><code class="language-python">def translate(language, number):
    match language:
        case &quot;English&quot;:
            result = yield from number_to_english(number)
        case &quot;Tamil&quot;:
            result = yield from number_to_tamil(number)
        case _:
            raise ValueError(f&quot;Language {language} is not supported&quot;)

    print(f&quot;{number} in {language} is {result}&quot;)
</code></pre>
<p>Notice how the <code>yield from</code> syntax makes the code a lot more straightforward.</p>
<p>Let's use this in <code>main()</code> now.</p>
<pre><code class="language-python">def main():
    scheduler = Scheduler()
    for x in range(1, 4):
        scheduler.add_task(translate(&quot;Tamil&quot;, x))
        scheduler.add_task(translate(&quot;English&quot;, x))
        scheduler.run()

main()
</code></pre>
<p>We can also add all the tasks first, and then run all the tasks at once:</p>
<pre><code class="language-python">def main():
    scheduler = Scheduler()
    for x in range(1, 4):
        scheduler.add_task(translate(&quot;Tamil&quot;, x))
        scheduler.add_task(translate(&quot;English&quot;, x))
    scheduler.run()

main()
</code></pre>
<p>Output (order may vary):</p>
<pre><code>3 in English is three
1 in Tamil is onnu
1 in English is one
2 in Tamil is rendu
2 in English is two
3 in Tamil is moonu
</code></pre>
<p>We have built a scheduler that runs concurrently!</p>
<h3>Keeping track of return value</h3>
<p>We did it, but we aren't keeping track of the return values of tasks.
To fix this problem, we add another level of indirection.</p>
<blockquote>
<p>All problems in computer science can be solved by another level of indirection.</p>
</blockquote>
<p>We build a <code>Task</code> wrapper for our coroutines. This will have add two things on top of coroutines:</p>
<ul>
<li>a <code>.status</code> attribute which can be <code>None</code>, <code>queued</code>, <code>running</code>, or <code>finished</code>.
</li>
<li>a <code>.value</code> attribute which will store its return value
</li>
</ul>
<p><code>task.value</code> is only vaild when <code>task.status</code> is <code>&quot;finished&quot;</code>.</p>
<p>The scheduler will be responsible for setting <code>.status</code> and <code>.value</code>.</p>
<pre><code class="language-python3">class Task:
    def __init__(self, coro, status=None):
        self.coro = coro
        self.status = status
        self.value = None

    def get_value(self):
        if self.status == &quot;finished&quot;:
            return self.value
        raise Exception(&quot;Task has not finished&quot;)
</code></pre>
<p>Change scheduler to work with tasks instead of coroutines:</p>
<pre><code class="language-python3">class Scheduler:
    def __init__(self):
        self.task_q = queue.Queue()

    def add_task(self, task):
        assert isinstance(task, Task), &quot;task must be an instance of Task&quot;

        task.status = &quot;queued&quot;
        self.task_q.put(task)

    def run(self):
        while not self.task_q.empty():
            task = self.task_q.get()
            task.status = &quot;running&quot;
            try:
                task.coro.send(None)
            except StopIteration as e:
                task.value = e.value
                task.status = &quot;finished&quot;
            else:
                self.task_q.put(task)
                task.status = &quot;queued&quot;
</code></pre>
<p>Let's see this working in an example:</p>
<pre><code class="language-python3">def translate_big_number(language, n):
    match language:
        case &quot;English&quot;:
            translator = number_to_english
        case &quot;Tamil&quot;:
            translator = number_to_tamil
        case _:
            raise ValueError(f&quot;Language {language} is unsupported&quot;)

    digits = [int(d) for d in str(n)]
    result = &quot;&quot;
    for digit in digits:
        result += yield from translator(digit)
        result += &quot; &quot;

    return result
</code></pre>
<p>We added a new function <code>translate_big_number</code>, which translates a number with multiple digits.</p>
<pre><code class="language-python3">def main():
    scheduler = Scheduler()

    for x in [12, 133, 120]:
        task1 = Task(translate_big_number(&quot;English&quot;, x))
        task2 = Task(translate_big_number(&quot;Tamil&quot;, x))

        scheduler.add_task(task1)
        scheduler.add_task(task2)
        scheduler.run()

        print(f&quot;{x} in English is {task1.value}&quot;)
        print(f&quot;{x} in Tamil is {task2.value}&quot;)
</code></pre>
<p>Output:</p>
<pre><code>12 in English is one two
12 in Tamil is onnu rendu
133 in English is one three three
133 in Tamil is onnu moonu moonu
121 in English is one two one
121 in Tamil is onnu rendu onnu
</code></pre>
<blockquote>
<p><strong>Bonus</strong> Can you build a coroutine <code>sleep(duration)</code> that sleeps, and allows other tasks to run for a certain duration?</p>
</blockquote>
<p>In <code>asyncio</code>, the scheduler is known as the event loop. Besides tasks, it also has &quot;Futures&quot;, which are analogous to
JavaScript promises. Read more about it <a href="https://realpython.com/async-io-python/#the-event-loop-and-asynciorun">here</a>.</p>
<hr />
<h2>Conclusion</h2>
<p>We looked at how to build coroutines, how they can be run concurrently, and how tasks can wait for other tasks to complete.</p>
<p>To get the most out of this, we need to be able to actually perform I/O asynchronously. This can be done purely in Python
with the <a href="https://docs.python.org/3/library/selectors.html#examples"><code>selectors</code></a> module. This is out of scope for this
blog.</p>
<p>Programming language-level support for lightweight threads has a lot of benefits to offer. Besides better performance on
blocking I/O, it simplifies writing concurrent code which is notoriously hard to write correctly and debug.
Coroutines need not run on a single OS thread. In other languages like Go and OCaml, they can and do run on multiple OS
threads as well.</p>
]]></description>
      <pubDate>2023-04-21T15:25:46+05:30</pubDate>
      <category>tech</category>
      <category>long</category>
      <category>python</category>
    </item>
    <item>
      <title>What do I self-host?</title>
      <link>/posts/what-i-self-host/</link>
      <guid>/posts/what-i-self-host/</guid>
      <description><![CDATA[<p>I've been recently deploying apps rather frequently on my personal droplet that I use for self-hosting. I tried hosting some
bulkier apps and I was surprised when they ran without crashing or bothering any of the other apps that were already deployed.</p>
<p>I was impressed by this setup, and I hope that writing this post inspires confidence in others to self-host their own software.</p>
<hr />
<h3>VPS Information</h3>
<p>I have a single $6 droplet and a 50GB volume mounted to it that costs $5. I reckon most people would be able to do
without needing that extra 50GB, but I have to mention it for the sake of completeness.</p>
<p>I have allocated 4GB of disk space for swap. I did that while attempting to host a minecraft server (and not succeeding),
but I left it that way. My RAM usage usually doesn't exceed 2GB in total, but I host apps like
<a href="https://github.com/plausible/analytics">Plausible</a> (Website Analytics) and
<a href="https://github.com/matrix-org/synapse">Synapse</a> (Matrix server), which recommend having 2-4GB of RAM.</p>
<ul>
<li>Memory: 1GB
</li>
<li>CPU: 1vCPU (Regular, not the Premium Intel/AMD offering)
</li>
<li>Disk: 25GB Disk + 50GB mount
</li>
<li>Datacenter: Bangalore
</li>
<li>OS: Ubuntu 20.04 (LTS)
</li>
<li>Cost per month: $6 (Droplet) + $5 (Extra disk space) = $11
</li>
</ul>
<h3>Software that I self-host</h3>
<p>| # | App | Description | Deployment method |
| - |-------|-----|-------------|-------------------|
| 1 | <a href="https://kaustubh.page">My blog</a> | Static website built with Hugo | Static with Caddy |
| 2 | <a href="https://github.com/plausible/analytics">Plausible</a> | Website analytics | docker-compose |
| 3 | <a href="https://github.com/zadam/trilium">Trilium</a> | Notetaking | docker-compose |
| 4 | <a href="https://github.com/usememos/memos">Memos</a> | Notetaking | docker-compose |
| 5 | <a href="https://github.com/miniflux/v2">Miniflux</a> | RSS Reader | supervisor |
| 6 | <a href="https://github.com/gotson/komga">Komga</a> | Manga Reader | supervisor |
| 7 | <a href="https://github.com/matrix-org/synapse">Synapse</a> | Matrix homeserver | docker-compose |
| 8 | <a href="https://github.com/mautrix/telegram">Mautrix-Telegram Bridge</a> | Matrix-Telegram Bridge | docker-compose |
| 9 | <a href="https://github.com/nikochiko/otto">Otto</a> | Cute doggo discord bot that plays music and chess | supervisor |
| 10 | <a href="https://github.com/nikochiko/otto">Otto-web</a> | Web app for otto | supervisor |
| 11 | <a href="https://mine.kaustubh.page">Minesweeper</a> | Fully client-side minesweeper | Static with Caddy |
| 12 | <a href="https://self-hosting-101.kaustubh.page">Self-hosting 101 Slides</a> | Notes from my workshop, built with mdbook | Static with Caddy |
| 13 | ... more slides | More static sites from other talks | Static with Caddy |</p>
<h3>Deployment tools</h3>
<ul>
<li><a href="https://github.com/caddyserver/caddy"><strong>Caddy</strong></a>: Webserver for <code>reverse_proxy</code> and serving static sites.
</li>
<li><a href="https://supervisord.org/"><strong>supervisor</strong></a>: For running services that I need to define (not systemd)
</li>
<li><a href="https://github.com/docker/compose"><strong>docker-compose</strong></a>: To deploy more complex apps. I use <code>restart: unless-stopped</code> or <code>restart: always</code> to automate restart on rebooting crashes
</li>
<li><strong>Makefile / rsync</strong>: To write <a href="https://github.com/nikochiko/kausm.in/blob/7b5e33e2656128a7f00d69da978dc222087052ca/Makefile#L5-L9">deployment scripts</a>
</li>
<li><strong>PostgreSQL</strong>: used by some of the apps
</li>
<li><strong>Redis</strong>: Used by some of the apps
</li>
</ul>
<p>supervisor configuration is simple enough that I can define all my services in the same <code>/etc/supervisord.conf</code> file
(unlike systemd). This is a huge help because I can copy paste configuration from the same file. It is easy to understand
so I know what to change.</p>
<p>The same is true for Caddy. I configure all my public sites in <code>/etc/caddy/Caddyfile</code>. Most website entries only need 3-4
<em>words</em> of configuration. This file also acts as a registry that I can refer to, to know which sites I have deployed and
disabled. As an added plus, I don't have to manually configure SSL for sites deployed on Caddy. I like this much more than
nginx and apache, where configuring new sites takes up multiple steps, and the Certbot SSL routine has to be repeated each
time.</p>
<p>I use docker-compose instead of supervisor when a project has messy dependencies. It is easy to setup and I don't have to
add supervisor entry for it. I find this convenient. I use <code>docker ps</code> to know which containers are running. I use volume
mounts so that apps that use more disk space can use the larger 50GB disk.</p>
<p><code>rsync</code> is very handy for deployments. I first heard about it after I had already had some experience hosting apps by hand
on servers, and I felt stupid about all the hacks I had used before that to transfer files between my computer and the VPS.
You can read more about it <a href="https://www.thegeekstuff.com/2010/09/rsync-command-examples/">here</a>.</p>
<hr />
<p>While this is already a cost-effective setup, I want to move to actually hosting apps
on my own hardware in the future. I've been putting it off because I've been on the
move a lot recently. I recommend that even more if you can do it.</p>
<p>It's been a rewarding journey for me. I have rotated different tools before settling on these, and I'm sure I'll continue to explore
more.</p>
<p>Ciao! 👋</p>
]]></description>
      <pubDate>2023-04-17T23:19:16+05:30</pubDate>
      <category>tech</category>
      <category>self-hosting</category>
    </item>
    <item>
      <title>What I Learnt From SICP</title>
      <link>/posts/what-i-learnt-from-sicp/</link>
      <guid>/posts/what-i-learnt-from-sicp/</guid>
      <description><![CDATA[<p>I had started with <a href="https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/index.html">SICP</a> (Structure and Interpretation of Computer Programs) some time early last year. I had read through one and a half chapters and then stopped because it felt too
mathematical and I didn't know what I was getting from it. Then later that year, I attended
<a href="https://anandology.com/lambda-retreat/">Lambda Retreat</a>, conducted by Anand, who is my colleague and has
found SICP to be quite profound himself.</p>
<p>It changed the way I think about programs, added new perspective, and got me interested in the design and implementation of programming
languages. I have heard varied opinions from people who have read this SICP. Some feel that it is overrated and for some it changes how
they think about programs.</p>
<p>To some extent, it was the latter for me. This post highlights what I had to take away from the book that was positive for me.</p>
<h2>Controlling complexity with abstractions</h2>
<p>This, and the following point, were the biggest take aways for me. It started with the
<a href="https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/full-text/book/book-Z-H-11.html#%_thm_1.10">exercise problem on Ackermann function</a>.
First I tried simplifying it the dumb way by expanding all the values. I noticed a pattern building up but it took a while to see what it
was doing. The expansions were getting harder and harder for higher values of the first argument. Then I abstracted out <code>A(m-1, n)</code> as a
function <code>f(n)</code> and it was straightforward after that.</p>
<p>Abstractions play a big role in the cognitive complexity of something. It would have taken a very long time for me to see the pattern
if I had not created this abstraction. We encounter this all the time in programming. If we don't keep two simple chunks separate
from one another, they form one big complex thing. Next time we want to make a change to this monstrosity, we have to wade through the
details and redo all the thinking that we did the first time.</p>
<h2>Building composable software with layers of abstraction</h2>
<p>Any non-trivial software will have layers to it. For example, a database system could have a file system layer, an engine layer, and a
user interface layer. If we want to add a new operation to the engine, we shouldn't have to implement something at the file system
layer. It is much better if we can re-use some part of the existing file system operations in our new operation.
This way we don't have to switch contexts between the engine logic and the details of file system manipulation. It is easier to focus only
on one.</p>
<pre><code>+--------------+
|  interface   |
+--------------+
|    engine    |
+--------------+
|  filesystem  |
+--------------+
</code></pre>
<p>This is only possible if our software doesn't have circular dependencies between layers. An explicit choice has to be made so that our
file system layer logic is limited in scope only to file system level manipulations. It shouldn't implement procedures that are specially
meant to perform <em>everything that engine operation X will do</em>. It should rather be the responsibility of the engine level code to pick
the right tools from the file system layer and build its logic.</p>
<p>For example, an <code>insert</code> operation in the engine might need to 1) find the right place to write the new contents, 2) serialize data into
that format, 3) write it there. The file system part should provide an API to do 1 and 3 separately, rather than combining the logic of
all three into one single procedure.</p>
<p>That way, each procedure has one responsibility and the dependency is well-defined. The same file system primitives can also be re-used to
implement other operations. We don't think of the specific logic in the engine layer when writing logic in the file system layer. We build
primitives that any engine-level operation could use.</p>
<p>SICP looks at this in a more programming languages oriented manner. In any programming language, <a href="https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/full-text/book/book-Z-H-10.html#%_sec_1.1">there are three parts: primitives,
means of abstraction, and means of combination</a>.
Each layer of abstraction should, in a way, provide these things to the next layer of abstraction.</p>
<h2>Eval-Apply: the wizardry of the wizard book</h2>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/9/9d/SICP_cover.jpg" alt="SICP cover image" /></p>
<p>The <a href="https://upload.wikimedia.org/wikipedia/commons/9/9d/SICP_cover.jpg">cover image of SICP</a> shows a wizard holding the eval-apply globe.
It is how programming is done. Evaluation happens as a combination of applications, and applications happens as a combination of evaluations.
Application refers to the application of functions, and evaluation refers to the evaluation of primitive statements. Combination happens
through application of functions. That the core of programming can be described with only two procedures is mind-blowing.</p>
<p>Function application is a two step process: evaluate the parameters, and then evaluate the body with the obtained values as arguments.
Evaluation is a series of applications.</p>
<p>By the end of the book, when I had implemented the evaluator, it felt very simple. A whole programming languages implemented with only
some functions. I think the implementation seemed simple because we were guided through building good abstractions and layers of
abstractions in a bottom-up manner. Doing the same thing without that context would have been much more complicated. It shows the power
of breaking down the problem in terms of primitives, the means of combination, and the layers of abstractions.</p>
]]></description>
      <pubDate>2023-03-08T23:03:30+05:30</pubDate>
      <category>tech</category>
    </item>
    <item>
      <title>Self Hosting 101 Workshop at FOSSMeet &apos;23</title>
      <link>/posts/self-hosting-101/</link>
      <guid>/posts/self-hosting-101/</guid>
      <description><![CDATA[<figure align="center">
    <img src="https://media.kaustubh.page/fossmeet-momento.jpg" />
    <figcaption>Me (left) accepitng a momento from Hadif (right), one of the two NITC students on my team</figcaption>
</figure>
<p>On Feb 11, I conducted a 3 hour introductory workshop on Self Hosting
at <a href="https://www.fossmeet.net/">FOSSMeet '23</a> at NIT Calicut. This post
has my notes on teaching Self-Hosting to a crowd and an easy-to-find
link for people who land on my personal website.</p>
<p>My slides for the workshop are available <a href="https://self-hosting-101.kaustubh.page">here</a>.</p>
<h3>Audience</h3>
<p>The audience for the event was mostly smart students who were relatively
new to the Linux terminal. Rather surprisingly, I got to know after the
workshop that some of the attendees were not even coming from a tech
background.</p>
<p>I think this is what should be expected for future workshops too. The
people who will benefit the most from an introductory workshop to Self Hosting
are people who have some familiarity with the Linux terminal but things
like SSH, tmux, nginx configuration might be new to them.</p>
<h3>Structure / Infra</h3>
<p>The structure of the workshop was that each participant would get a DigitalOcean droplet
allotted to them for the day. We had set up these droplets with a non-root
sudo user, and a default password for all instances that they could use.
Agreed that while even things like creating a non-root user would be
useful to someone looking to self-host, it is better to include things like
that in an appendix and use the workshop time on more interesting
things.</p>
<p>The DNS entries were set for all nodes (an A record) for a subdomain
and the corresponding wildcard for it (e.g. <code>alpha.k8x.in</code> and
<code>*.alpha.k8x.in</code> would point to the same node).
Authentication was done with a password, with SSH authentication
disabled.</p>
<p>After this, we would go over SSH, tmux, apt, Nginx, static hosting,
dynamic hosting, HTTPS with certbot, persistent services, and htop.
Each of these would include a small exercise that the participants
will need to do. The workshop team would then go around the participants,
help whoever might be stuck and then keep going.</p>
<p>The highlight of the workshop was hosting a static website and a
dynamic website, enabled from HTTPS.</p>
<h3>What went well?</h3>
<p>It was a crowded workshop and we had to say no to some interested
participants because we had only prepared 40 nodes for the
workshop. Participants showed interest and there were attendees
with different levels of familiarity attending.</p>
<p>During the tasks, the three of us (me and two student volunteers
from NITC) on the conducting team used to walk around different
areas, help participants, and answer queries. This worked very well
because a lot of little things could go wrong and having an
experienced person to fix things on the spot meant that the
participants weren't getting stuck anywhere.</p>
<p>For example, when we
were using certbot to apply HTTPS, some participants made a typo
in the domain and certbot wrote a new configuration to the Nginx
default configuration, rather than the site it was intended to be
on. This configuration needed to be restored back to normal and
the website configuration had to be fixed. I had to do this for
multiple participants and it wouldn't have been easy to fix if
someone experienced hadn't been there on the spot.</p>
<p>At the end, it made me very happy that almost all of the
participants had stuck through the 3 hours and had deployed both
the static and dynamic websites. Some of these participants were
new even to SSH, <code>cat</code> and <code>nano</code> commands when they started, and
by the end they knew their way around tmux and nginx.</p>
<p>It helped to keep the things to cover very limited. I didn't go
into docker and docker-compose, vim, Makefiles, and other things
I had initially wanted to cover. That was the right call. I had
my notes reviewed by my colleague <a href="https://anandology.com">Anand</a>,
who has conducted such trainings before, and that helped me set
the goals accordingly.</p>
<p>Publishing the notes to a website helped because then participants
could catch up on things they had forgotten. They could also use it
later at home to remember what they learnt in case they decide to
start self-hosting on their own. An easy to pronounce, short link
makes it even better.</p>
<p><code>mdbook</code> worked very well for my slides.</p>
<h3>What could've been better?</h3>
<p>Self-hosting also refers to using your own hardware at home to
host software and exposing it to the web. A workshop on that kind
of self-hosting is hard to conduct at scale, and it's easier to
get started with cloud machines. Some participants came to me later
saying that they expected this kind of self-hosting rather than
relying on cloud vendors. I should've stated it at the start to
set the expectations of participants on what kind of self-hosting
we would be talking about.</p>
<p>While most of the content in my notes was correct, there were 2
times that I forgot to include a line of code somewhere and 1
task where I had only mentioned a step in English
text and not as code. I was able to get most students to see my
correction, but some still missed them. Then I had to repeat it
multiple times and help some participants personally on it. Typos
or forgotten lines of code like this are easily mendable in small
groups but they become much more expensive as the number of
participants scales.</p>
<p>Some participants mentioned to me later that the pace of the
workshop was quite slow for them, and that it was beginner
oriented. While I think that was the right pace anyway while
keeping the whole crowd in mind, it might have helped to have
some fun bonus tasks
along with normal tasks so that the participants remained
engaged.</p>
]]></description>
      <pubDate>2023-02-16T20:06:32+05:30</pubDate>
      <category>tech</category>
      <category>self-hosting</category>
    </item>
    <item>
      <title>Objects and Types in Python</title>
      <link>/posts/objects-and-types-in-python/</link>
      <guid>/posts/objects-and-types-in-python/</guid>
      <description><![CDATA[<p>Python's type system is fascinating. Everything in Python (anything that can be given a name or passed as an argument) is an object. This includes primitives (such as <code>int</code>, <code>str</code>, <code>bool</code> objects), compound objects (made from handwritten classes), and very interestingly the classes or types themselves. This fact that everything in Python is derived from a common base makes it very powerful because any two of these objects can be combined or extended in similar ways. However, the implementation of this idea has consequences. It sometimes gives rise to confusing behaviours that can be hard to reason about if you only understand the behaviour intuitively. </p>
<p>In this post, we will go over what this implies, understand the type-system bottom-up, and then use that understanding to reason about some confusing behaviours. By the end of this post, you will have a clear picture of what classes and objects really are and what lies behind this abstraction.</p>
<h2>Table of contents</h2>
<ol>
<li><a href="#the-chicken-egg-problem">The Chicken-Egg problem</a>
<ul>
<li><a href="#object-and-type"><code>object</code> and <code>type</code></a>
</li>
<li><a href="#relationship-between-object-and-type">relationship between <code>object</code> and <code>type</code></a>
</li>
<li><a href="#pyobject-and-pytypeobject"><code>PyObject</code> and <code>PyTypeObject</code></a>
</li>
</ul>
</li>
<li><a href="#method-lookup">Method lookup</a>
<ul>
<li><a href="#lookup-order-and-setting-and-deleting-attributes">Lookup order, and setting and deleting attributes</a>
</li>
<li><a href="#self-injection"><code>self</code> injection</a>
</li>
<li><a href="#descriptors">Descriptors</a>
</li>
<li><a href="#crafting-methods-by-hand">Crafting methods by hand</a>
</li>
</ul>
</li>
<li><a href="#classes-as-objects">Classes as objects</a>
<ul>
<li><a href="#creating-classes-with-type">Creating classes with <code>type</code></a>
</li>
<li><a href="#class-keyword-as-syntactic-sugar"><code>class</code> keyword as syntactic sugar</a>
</li>
<li><a href="#note-on-recreating-the-class-keyword-with-functions">Note on recreating the <code>class</code> keyword with functions</a>
</li>
</ul>
</li>
</ol>
<ul>
<li><a href="#conclusion">Conclusion</a>
</li>
<li><a href="#footnotes">Footnotes</a>
</li>
</ul>
<blockquote>
<p>Note: I drafted an updated version of some of the explanations for a more recent talk. It drops the 2nd section on
method lookup, but I believe it has a better introduction into the chicken-egg problem and creating objects as
classes. The JupyterLab slides can be found <a href="https://kaustubh.page/slides/objects-and-types-notebook-2.html">here</a>.
You can scroll to move between slides.</p>
</blockquote>
<h2>The Chicken-Egg Problem</h2>
<h3><code>object</code> and <code>type</code></h3>
<p>When people say that everything is an object in Python, they mean that very literally. There is an <code>object</code> class that all Python objects are instances of. This is also the implicit base class when you create a new class.</p>
<pre><code class="language-python">&gt;&gt;&gt; isinstance(5, object)
True
&gt;&gt;&gt; isinstance(int, object)
True
&gt;&gt;&gt; isinstance(&quot;abcdef&quot;, object)
True
</code></pre>
<p>Classes themselves are objects too. This is slightly counter-intuitive when you think of an object
as an instance of some class. But it is very much an object in that you can pass it around
as a function argument, name it differently, set attributes and call methods on it.</p>
<p>All classes are instances of the <code>type</code> class. In that sense, <code>type</code> is a metaclass and other
metaclasses need to inherit from <code>type</code>. <a href="#footnotes"><sup>1</sup></a></p>
<h3>Relationship between <code>object</code> and <code>type</code></h3>
<p>There are three things about this that are confusing when considered together:</p>
<ol>
<li><code>object</code> is a <em>class</em>
</li>
<li>All classes are objects of the type <code>type</code>.
</li>
<li><code>type</code> itself is a class
</li>
</ol>
<p>We can verify this in the Python interpreter.</p>
<p>If an <code>object</code> is a class, it should be an instance of the <code>type</code> class.</p>
<pre><code class="language-python">&gt;&gt;&gt; isinstance(object, type)
True
</code></pre>
<p><em>The interpreter agrees.</em></p>
<p>To verify #2, we will create a custom class and check whether it's an instance of <code>object</code>. Then we'll also verify whether it is an instance of <code>type</code>.</p>
<pre><code class="language-python">&gt;&gt;&gt; class A:
...   pass
...
&gt;&gt;&gt; isinstance(A, object)
True
&gt;&gt;&gt; isinstance(A, type)
True
</code></pre>
<p><em>Checks out.</em></p>
<p>Now let's see if <code>type</code> is a class. For this, we will check whether <code>type</code> is an instance of <code>type</code> and whether <code>type</code> is an <code>object</code>.</p>
<pre><code class="language-python">&gt;&gt;&gt; isinstance(type, type)
True
&gt;&gt;&gt; isinstance(type, object)
True
</code></pre>
<p><em>See, I wasn't lying.</em></p>
<p>What's happening here? type is a type, type is also an object, and object is itself a type.</p>
<p>We can understand this relationship better with <code>issubclass</code>:</p>
<pre><code class="language-python">&gt;&gt;&gt; issubclass(type, object)
True
&gt;&gt;&gt; issubclass(object, type)
False
</code></pre>
<p>While <code>type</code> is a subclass of <code>object</code>, <code>object</code> isn't a subclass of <code>type</code>.
Instead, only the <em>type</em> of <code>object</code> is <code>type</code>. That is, an <code>object</code> is an <em>instance</em> of <code>type</code>.</p>
<p>This diagram captures this relationship accurately:</p>
<p><img src="/objects-and-types-in-python/object-type-excalidraw.png" alt="" /></p>
<p>Each object has a type, some other metadata, and its attributes associated to it. A type itself
has everything that an object does (including a type), along with some extra fields such as
the base classes that it borrows from.</p>
<p>This relationship can be better understood when we take a look at how it is implemented
in CPython.</p>
<h3><code>PyObject</code> and <code>PyTypeObject</code></h3>
<p>In the CPython implementation, there are two basic structs for <code>PyObject</code> and <code>PyTypeObject</code>
that look kind of like this. Notice that the <code>_object</code> struct has a pointer to its type
(<code>*ob_type</code>), and there is a separate <code>bases</code> tuple in <code>_typeobject</code> that stores the bases
for a type object.</p>
<pre><code class="language-c">// a simplified struct definition for PyObject and PyTypeObject

#define PyObject_HEAD       PyObject ob_base;

typedef struct _object PyObject;
typedef struct _typeobject PyTypeObject;

struct _object {
    PyTypeObject *ob_type;
    // and other metadata
};

struct _typeobject {
    PyObject_HEAD  // macro to &quot;inherit&quot; from PyObject
    const char  *name;
    PyObject    *bases;
    // and other things
};
</code></pre>
<p>The <code>ob_type</code> of <code>object</code> is manually set to <code>type</code>, and this <code>object</code> is then added to the
<code>bases</code> tuple in <code>type</code>.</p>
<p>To make it clear, let's try to write the C code that will represent such a relationship:</p>
<pre><code class="language-c">// let's assume this function is defined and initialises a tuple with a single PyObject
PyObject* tuple_init(PyObject*);

PyTypeObject type = { .name = &quot;type&quot; };
type.ob_base.ob_type = &amp;type;

PyTypeObject object = { .name = &quot;object&quot; };
object.ob_base.ob_type = &amp;type;

// type.bases needs to be set to a Python Tuple
type.bases = tuple_init((PyObject*) &amp;object);
</code></pre>
<p>wtfpython has a good explanation on this <a href="https://github.com/satwikkansal/wtfpython#-the-chicken-egg-problem-">here</a> and CPython has comments explaining this too: <a href="https://github.com/python/cpython/blob/a286caa937405f7415dcc095a7ad5097c4433246/Include/object.h#L24-L29">https://github.com/python/cpython/blob/a286caa937405f7415dcc095a7ad5097c4433246/Include/object.h#L24-L29</a></p>
<h2>Method Lookup</h2>
<h3>Lookup order, and setting and deleting attributes</h3>
<p>This section sets up the context for the next one about method lookup.</p>
<p>The lookup order in Python goes like Object → Class → Base classes. However, the setting and deleting of attributes only happens directly on the object itself.</p>
<p>Take this example where we initialise an object whose class has an attribute <code>x</code>, and then
try to delete it:</p>
<pre><code class="language-python">&gt;&gt;&gt; class A:
...   x = 10
...
&gt;&gt;&gt; a = A()
&gt;&gt;&gt; print(a.x)
10
&gt;&gt;&gt; del a.x
AttributeError: x
</code></pre>
<p>The attributes of a class can be looked up by its objects, but cannot be deleted from there.</p>
<p>However, deleting directly from the class will work fine:</p>
<pre><code>&gt;&gt;&gt; del A.x  # is fine
&gt;&gt;&gt; print(a.x)
AttributeError: 'A' object has no attribute 'x'
</code></pre>
<p>The same happens when setting attributes too. We can see it in this example where we set <code>x</code>
on both the object and the class, and then delete the <code>x</code> on the object:</p>
<pre><code class="language-python">&gt;&gt;&gt; class A:
...   x = 10  # set on class
...
&gt;&gt;&gt; a = A()
&gt;&gt;&gt; a.x = 20  # set on object
&gt;&gt;&gt; print(a.x)
20
&gt;&gt;&gt; del a.x  # delete from object
&gt;&gt;&gt; print(a.x)  # doesn't raise attribute error because A.x is still there
10
</code></pre>
<p>Even though we deleted <code>a.x</code>, <code>A.x</code> was still there and that's the value we got when we printed <code>a.x</code> the second time.</p>
<p>We can observe that the lookup is dynamic and any modifications to the attributes on the class
should reflect when we perform lookup from the object.</p>
<h3><code>self</code> injection</h3>
<p>When we define methods inside a class body, we take the bound object as the first argument <code>self</code>.
This is strange because <code>self</code> is defined when the method is called from an initialised object,
but not when the method is called from the class itself.</p>
<pre><code class="language-python">&gt;&gt;&gt; class A:
...   def hello(self):
...     print(&quot;Hello, world!&quot;)
...
&gt;&gt;&gt; A.hello()
TypeError: A.hello() missing 1 required positional argument: 'self'
&gt;&gt;&gt; a = A()
&gt;&gt;&gt; a.hello()
Hello, world!
</code></pre>
<p>Additionally, this <code>self</code> injection does not happen at the time that the function is called, because
Python also allows you to assign a different variable to this method and it still works:</p>
<pre><code class="language-python">&gt;&gt;&gt; a = A()
&gt;&gt;&gt; f = a.hello
&gt;&gt;&gt; f()
Hello, world! 
</code></pre>
<p>So what's the magic here?</p>
<p>If we inspect the types of <code>A.hello</code> and <code>a.hello</code>, we'll see the difference.</p>
<pre><code>&gt;&gt;&gt; type(A.hello)
&lt;class 'function'&gt;
&gt;&gt;&gt; type(a.hello)
&lt;class 'method'&gt;
</code></pre>
<p>They're not the same, and <code>a.hello</code> is a <code>method</code> and not a <code>function</code>.</p>
<p>The <code>hello</code> attribute returns different values depending on if it is called from the class or an initialised object. Python allows such behaviour with descriptors.</p>
<h3>Descriptors</h3>
<p>Python descriptors are classes that have <code>__get__</code>, <code>__set__</code>, or <code>__del__</code> methods. So instead
of the actual value, these methods are called when a descriptor is accessed as an attribute. A
popular example of descriptors in use is the <code>property</code> decorator that can be used for dynamic
attributes.</p>
<p>Python functions are actually descriptors with a <code>__get__</code> method that returns a
<code>method</code> with the object and function bound to it. I'll take a couple of examples to
explain descriptors.</p>
<p>A simple Descriptor that always returns 10 would look like this <a href="#footnotes"><sup>2</sup></a>:</p>
<pre><code class="language-python">&gt;&gt;&gt; class Ten:
...   def __get__(self, obj, objtype=None):
...     return 10
...
&gt;&gt;&gt; class A:
...   x = 5
...   y = Ten()
...
&gt;&gt;&gt; a = A()
&gt;&gt;&gt; a.x
5
&gt;&gt;&gt; a.y
10
</code></pre>
<p>A descriptor has access to the object it is called with from the <code>obj</code> argument. We could write
this descriptor to return the square of <code>obj.x</code>:</p>
<pre><code class="language-python">&gt;&gt;&gt; class SquareX:
...   def __get__(self, obj, objtype=None):
...     return obj.x * obj.x
...
&gt;&gt;&gt; class A:
...   y = SquareX()
...
...   def __init__(self, x):
...     self.x = x
...
&gt;&gt;&gt; a = A(5)
&gt;&gt;&gt; a.y
25
</code></pre>
<p>You can also directly try using the <code>__get__</code> method on a function. Calling it with the object
as its argument will return a method that binds this function and the object.</p>
<h3>Crafting methods by hand</h3>
<p>This is a side section, but rather interesting because we can directly observe how methods are
actually represented differently from functions.</p>
<p>The <code>method</code> class isn't available as a builtin, but you can get it by calling <code>type()</code> on a method. This class takes a function and an object during initialisation, in that order. We can use that to create methods by hand that were never bound to the class or the object.</p>
<pre><code class="language-python">&gt;&gt;&gt; class A:
...   greeting = &quot;Hello, I am Foo Bar.&quot;
...   def hello(self):
...     pass
...
&gt;&gt;&gt; a = A()
&gt;&gt;&gt; method_class = type(a.hello)
&gt;&gt;&gt; def greet(self):
...   print(self.greeting)
...
&gt;&gt;&gt; greet_with_a = method_class(greet, a)
&gt;&gt;&gt; greet_with_a()
Hello, I am Foo Bar.
</code></pre>
<p>We just crafted a method that was never an attribute.</p>
<h2>Classes as Objects</h2>
<h3>Creating classes with <code>type</code> </h3>
<p>Everything is an object, and classes are not exempt from it. We saw earlier that classes are instances of the <code>type</code> class.</p>
<p>We can also create classes with the <code>type</code> class initialiser, similar to other objects. For example:</p>
<pre><code class="language-python">&gt;&gt;&gt; def foo(self):
...   return &quot;bar&quot;
...
&gt;&gt;&gt; A = type(&quot;A&quot;, tuple(), {&quot;foo&quot;: foo, &quot;x&quot;: 10})
&gt;&gt;&gt; a = A()
&gt;&gt;&gt; a.foo()
'bar'
&gt;&gt;&gt; a.x
10
</code></pre>
<p>The type signature of the 3-argument <code>type</code> initialisation is <code>type(name, bases, dict) → type</code>.</p>
<p>The <code>name</code> argument will be the value taken by <code>cls.__name__</code>, <code>bases</code> can be used to achieve
inheritance by providing a tuple of classes, and <code>dict</code> is a dict of key-value pairs that
are the attributes on this class.</p>
<p>When a metaclass needs to be used, that class can be used instead of <code>type</code>.</p>
<h3><code>class</code> keyword as syntactic sugar</h3>
<p>A syntactic sugar is a some additional syntax that makes it easier to express something but doesn't add a new feature to the language.</p>
<p>The <code>class</code> keyword can be seen as calling <code>type</code> with the variables defined in its scope as
value of the <code>dict</code> argument. In that sense, <code>class</code> is a a syntactic sugar for creating
instances of <code>type</code>. We'll write a <a href="https://realpython.com/primer-on-python-decorators/">decorator</a>
to show this that will mimick the behaviour of the <code>class</code> keyword as closely as possible.</p>
<p>At the end, we should be able to define classes with something like this:</p>
<pre><code class="language-python">@create_class()
def A():
	x = 10	
	def __init__(self, y, z):
	    self.y = y
	    self.z = z
	return locals()

a = A(5, 10)
</code></pre>
<p>We should also be able to perform inheritance.</p>
<pre><code class="language-python">@create_class(A)
def B():
	x = 20
	return locals()

b = B(5, 10)  # y and z, as in A.__init__
</code></pre>
<p>And use metaclasses:</p>
<pre><code class="language-python">@create_class(type)
def Meta():
    def __repr__(self):
        return f&quot;Meta {self.__name__}&quot;
	return locals()

@create_class(metaclass=Meta)
def C():
    pass
</code></pre>
<p>This syntax is very similar to the one used to declare classes with the <code>class</code> keyword, except that it is a <code>def</code>, has a <code>create_class</code> decorator, and returns <a href=""><code>locals()</code></a>.</p>
<p>The decorator gets the function object, and can access the parameters and default arguments. From there, we can resolve the variables from <code>globals()</code> and initialise the class.</p>
<p>Feel free to give it a try now before reading on with the solution.</p>
<p>Our decorator gets access to the function and we can inspect it to find out its parameters
(base classes) and other keyword arguments (metaclass). We will only get these parameters
as strings, and we need to resolve it using <code>globals()</code>. Then we can use the metaclass (by
default, <code>type</code>) to initialise this with the three arguments. We can assume that the function
will return the <code>locals()</code> dict from inside and use that for building the third argument
in initialisation.</p>
<pre><code class="language-python">def create_class(*bases, **kwds):
    def wrapper(func):
        metaclass = kwds.get(&quot;metaclass&quot;, type)
        func_locals = func()
        klass = metaclass(
            func.__name__, bases, func_locals,
        )
        return klass
    return wrapper
</code></pre>
<p>Now, the examples above will work and we can recreate the <code>class</code> keyword without using it.</p>
<p>These examples should work now:</p>
<pre><code class="language-python">@create_class()
def A():
    x = 20
    return locals()

@create_class(A)
def B():
    y = 30

    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    return locals()

@create_class(type)
def Meta():
    def __repr__(self):
        return f&quot;meta {self.__name__}&quot;
    return locals()

@create_class(B, metaclass=Meta)
def C():
    return locals()
</code></pre>
<p>We created a simple class, a subclass, a metaclass, and a class which uses that metaclass without
using a decorator that spans less than 10 lines of code.</p>
<p>We can even get rid of the need to return <code>locals()</code> from the definitions if we use something like <a href="https://docs.python.org/3/library/inspect.html#inspect.getsource"><code>inspect.getsource</code></a> and then performing an <a href="https://realpython.com/python-exec/"><code>exec</code></a> on the function body and supplying a local env, but <code>inspect.getsource</code> will actually read the literal text from the Python file to produce the output. I didn't want to get that hacky and stick to what is always available at runtime.</p>
<h3>Note on recreating the <code>class</code> keyword with functions</h3>
<p>One point to note with this is that the scoping in classes is different from scoping in
functions. Even though variables declared inside a <code>class</code> block are scoped to that block
only, the <code>class</code> block can readily access global variables.</p>
<p>It can get slightly confusing, but this is perfectly valid Python code:</p>
<pre><code class="language-python">x = 10

class A:
    x = x + 5
    print(&quot;x inside A:&quot;, x)  # x inside A: 15

print(&quot;x outside A&quot;, x)  # x outside A: 10
</code></pre>
<p>This is not possible with our way of defining classes because functions by default don't
get access to the global variables unless used with <code>nonlocal</code> or <code>global</code>.</p>
<h2>Conclusion</h2>
<ul>
<li>Because everything in Python is an object and all objects have a type, there exists a strange relationship between <code>object</code> and <code>type</code>
</li>
<li>We looked at how <code>type</code> is itself a <code>type</code> and how that relationship is defined in the interpreter
</li>
<li>We saw how Python uses descriptors to implement methods and how we can craft our own
</li>
<li>We blurred the line between classes and objects by eliminating the need for a special keyword for classes, and instead working with <code>type</code>.
</li>
</ul>
<p>The <em>objects all the way down</em> philosophy has its quirks, but it also makes Python very expressive. We can combine classes and functions in a similar manner as other primitive types. Lists and tuples need not be restricted to a single type and we can even have custom meta types. All of this is possible because there is some shared structure to all the objects that the interpreter can exploit. With the knowledge of a few rules, we can extend and combine anything in Python.</p>
<h2>Footnotes</h2>
<p><em>[1] In fact, Python allows you to define your own metaclasses. Then the class will be an instance of a different type other than the default that is</em> <code>type</code>. <em>Note that metaclasses also need to subclass from</em> <code>type</code>, <em>so the stated relationship between</em> <code>type</code> <em>and</em> classes <em>still holds.</em></p>
<p><em>[2] Example borrowed from</em> <a href="https://docs.python.org/3/howto/descriptor.html"><em>Descriptor HowTo Guide</em></a><em>, Python documentation.</em></p>
]]></description>
      <pubDate>2023-01-17T17:27:12+05:30</pubDate>
      <category>tech</category>
      <category>long</category>
      <category>python</category>
    </item>
    <item>
      <title>On Encoding and Decoding</title>
      <link>/posts/on-encoding-and-decoding/</link>
      <guid>/posts/on-encoding-and-decoding/</guid>
      <description><![CDATA[<p>I was in the shower and got thinking about how computers encode everything as numbers.</p>
<p>Around the time when I was in school and had some familiarity with programming, I didn't really think it was a good analogy to think of computers as sophisticated calculators. Calculators are dumb, they can only perform operations on numbers whereas computers are able to build something as complicated as the internet.</p>
<p>I was younger then. Now I am older and wiser and I can see why “computers are sophisticated calculators” and even appreciate the beauty of what it implies.</p>
<p>To a younger me, I would explain the idea like this:</p>
<blockquote>
<p>Padawan, you need to understand that computers are the epitome of applied mathematics.<br />
Why, you ask? Look around you. Everything you perceive, you can represent as math. Colors, bodies, collisions, and even movements.</p>
<p>You don't believe me? Take the movement of a falling box. Can you represent this as math?<br />
You can say there's a box that is 1m by height, width, and length. It is in free fall and its current velocity is 20 m/s. Acceleration due to gravity should be assumed to be 10 m/s2.</p>
<p>Now I tell you, you can represent that with math as 1112010. You see? I just put all the numbers together with the assumption that m, m/s, and m/s2 are our units. The first three digits are height, width, and length; in that order. Then there are two digits for current velocity, and two digits for the acceleration.</p>
<p>Now if I give you a different sequence, let's say 2343010, and tell you that this also represents a falling box in a similar way, will you be able to recreate the scenario?<br />
Good work, Padawan. Of course it is an unusually shaped large box with dimensions 2x3x4 in m. Its current velocity is 30 m/s and acceleration due to gravity is 10 m/s2.</p>
</blockquote>
<p>That is much closer to what a calculator does. These are numbers and we can combine them as we want. If we were asked to find out the time to collision if the two hypothetical boxes were thrown one after the other, we could do that with a calculator.</p>
<p>If the calculator is powerful enough, we can write the procedure once and use it multiple times with different values. Computer enough for you?</p>
<blockquote>
<p>Not yet? No? Ah, you're not convinced how I would teach a computer to understand and answer questions such as the one about collision. Good question.</p>
</blockquote>
<p>It's the same, really. No different, except the computer now understands something different. It will be easier to explain if we put words to what we did: we first <em>encoded</em> the motion of a box into a sequence of digits. We know how to <em>decode</em> it, but we now need to teach a computer that.</p>
<p>Once again, we will encode. This time, we will encode the <em>procedure of decoding</em> this falling-box format, and then the procedure for finding the collision time.</p>
<pre><code class="language-text-plain">010 111 212 323 524
</code></pre>
<p>This is my encoding. Each group of three has 3 digits. First digit represents the starting position in the sequence (starting from leftmost digit as 0th position), second digit represents the number of digits that need to be taken (notice it is 2 for the last 2 groups, for acceleration and velocity), and the third is its reference number. The reference number will allow us to use these quantities as variables.</p>
<p>This first part of the procedure decodes the data into variables. Now let's solve the collision problem. The slower box is dropped first, and the faster box is dropped later. We will ignore the dimensions of the boxes in our calculations.</p>
<p>If you do the math, you'll arrive at the result that the time to collision would be:</p>
<p>\(u_1t + \frac{1}{2}a_1t^2 = u_2t + \frac{1}{2}a_2t^2\)</p>
<p>\(2 *(u_1-u_2) + (a_1-a_2)*t = 0\)</p>
<p>\(t = 2 * \frac{(u_2 - u_1)}{a_1 - a_2}\)</p>
<p>Let's say this is the encoding for our math language that the computer understands this notation:</p>
<ul>
<li>Operations:
<ul>
<li>000 - addition
</li>
<li>001 - subtraction
</li>
<li>002 - multiplication
</li>
<li>003 - division
</li>
<li>004 - parenthesis open
</li>
<li>005 - parenthesis close
</li>
</ul>
</li>
<li>Operands:
<ul>
<li>1xx - with xx being a variable ID
</li>
<li>2xx - with xx being a constant number as decimal
</li>
</ul>
</li>
</ul>
<p>We can encode the above equation as:</p>
<pre><code class="language-text-plain">222 002 004 113 001 103 005 003 004 104 001 114 005
 2   *   (   u2  -   u1  )   /   (   a1  -   a2  )
</code></pre>
<p>That's how you would represent a procedure as data, in the form of sequences of digits.</p>
<p>Computers only understand bits because they communicate with base 2 numbers. The same ideas are applicable when they are converted from base10 to base2.</p>
<h2>Appreciation</h2>
<p>Isn't it wonderful that with such simple ideas of encoding and decoding we have today been able to invent things such as the internet?<br />
We have been able to use numbers to encode our words and social interactions so that they can be shipped to remote places and decoded there. It blows my mind.</p>
]]></description>
      <pubDate>2022-11-21T23:05:31+05:30</pubDate>
      <category>tech</category>
      <category>math</category>
    </item>
    <item>
      <title>What&apos;s Wrong With the Way We Think About Web Development?</title>
      <link>/posts/whats-wrong-with-the-way-we-think-about-web-development/</link>
      <guid>/posts/whats-wrong-with-the-way-we-think-about-web-development/</guid>
      <description><![CDATA[<p>Web development has an abstraction problem. The low-level implementation details
have leaked all the way into code we reguarly write. It's analogous to writing
C or assembly where you need to shape your thinking in terms of the underlying
architecture.</p>
<p>Good abstractions capture thought ideas with only as much bend as is absolutely
needed to eliminate ambiguity. The ideation of a web app is in terms of the
content the user can see and the interactions they can make with the website --
simple. When it goes to implementation however, the developer has to think
analytically and separate what goes into the frontend from what goes into
the backend. This feels very artifical and low-level.</p>
<p>Secondly, it's amazing how much HTML and CSS (or pseudo-CSS like Bootstrap/Tailwind/et al.)
we still write. How many times has a simple Bootstrap form been created by you? By
other developers? Yet we spend time doing it again, caring about its alignment and how it looks
on different screen sizes. All we do care about is what content is displayed, what data is
collected from a form, and what happens after submitting it. Context switching to low-level
details takes our attention away from these things.</p>
<p>Thirdly, our database abstractions are not there yet. Databases are part of our webapps
by necessity rather than choice. They bring complexity with them.
To maintain them, we have to keep a schema and migrations. We have to write SQL queries that
are performant and at the same time compatible with higher-level language abstractions.
It's a tricky problem to solve. ORMs bring in a different kind of complexity and often
end up hiding performance bottlenecks. For example when you want only the username, an ORM
will likely load all other user data in one shot. The magic they add could have side effects
like loading data from <code>JOINs</code> that won't be used later.</p>
<p>When I presented my thoughts to a group of developers coming in from varying backgrounds and
levels of experience, I was confronted with arguments that justified this complexity. My pipe
dream is one where a &quot;web developer&quot; without design expertise (me) is equipped with tools
to build complex, maintainable production grade web apps where they can focus on the important
parts only. Even with these counter-arguments, I felt that my frustration was justified and
in fact even shared by other developers. I wasn't happy with any of the existing solutions
that were mentioned -- Liveview, Hotwire, Isomorphic Javascript.</p>
]]></description>
      <pubDate>2022-11-04T21:44:01+05:30</pubDate>
      <category>tech</category>
    </item>
    <item>
      <title>On Camus&apos; &quot;The Stranger&quot;</title>
      <link>/posts/the-stranger/</link>
      <guid>/posts/the-stranger/</guid>
      <description><![CDATA[<p><em>This blog post tells my reflections on reading Camus' novel The Stranger.
I reference the content of the novel and it is unlikely to make sense
to you if haven't read the novel yourself. For a tl;dr of the novel,
I would recommend reading <a href="https://en.wikipedia.org/wiki/The_Stranger_(Camus_novel)">the Wikipedia entry</a>.</em></p>
<p>I've never read a novel like this before. There is only a series of events,
narrated with a hint of indifference. The murder of a man, Mersault's
separation from his fiance, his death sentence, and of course the
death of his mother. None of these incidents were profound enough to throw
the man into some odd kind of reflections on his life. He never regretted
any of his actions, and as he proved to the chaplain towards the end, he
didn't change his religious beliefs despite much attempt.</p>
<p>It was as if his every action was actually an inaction. No action was
his own &quot;wish&quot;. He didnt wish to kill the Arab, but it was the weather
and the Arab's knife that made him pull the trigger. He didn't wish to
write the letter to Raymond's girlfriend. That too, like Marie's marriage
proposal, he just agreed to without express enthusiasm. Yet at the end
it was these &quot;actions&quot;, and his indifference for which the jury imagined
motives (for the common people are obsessed with meaning and causation)
and eventually condemned him to death.</p>
<p>On the other hand, it is just absurd that the kind of arguments made to
put Mersault to death were not a crime on their own -- not wanting to see
her mother before burial, not paying respects, having coffee with milk,
smoking a cigarette. In fact, someone who even feigned interest in these
activities would've gotten a reduced sentence. Moreover, contrary to the
jury's opinion, he did in fact quote his mother several times and indeed
loved her. Rather than being honest, one is supposed to play the game
and should pay the price otherwise if they don't.</p>
]]></description>
      <pubDate>2022-10-06T00:55:26+05:30</pubDate>
      <category>non-tech</category>
      <category>personal</category>
    </item>
    <item>
      <title>Dead simple SMTP server for development and testing</title>
      <link>/posts/aiosmtpd/</link>
      <guid>/posts/aiosmtpd/</guid>
      <description><![CDATA[<h1>Local development setup</h1>
<p>When we have some code that sends emails, we probably also want a stub that we can use locally --
which won't send actual emails, let us inspect the content of each outgoing mail, and let us
conduct tests.</p>
<p>I needed something like this when we were building the skeleton of a guided project for a training
[[1]]({{&lt; relref &quot;#references&quot; &gt;}}).
One of the tasks was for the students to send an email notification upon some action.</p>
<p>We had two use-cases for an SMTP server like this here:</p>
<ol>
<li>To recommend to the students for trying their code locally, and
</li>
<li>To test that the emails were being sent once it is deployed.
</li>
</ol>
<p>For the first, I needed something simple to use and deploy locally that I could easily recommend to others
(and not have to spend time instructing them on how to configure XYZ).
For the second, I wanted something that gets the job done without having me pay for some fancy SaaS.
Luckily, I found a single solution that was good enough for both.</p>
<p>I had to only look at the standard library (<a href="https://docs.python.org/3/library/smtpd.html"><code>smtpd</code> package</a>)
to find what looked like the perfect solution -- something as simple as <code>python -m http.server</code>.
But the documentation there recommended using another library (an asyncio replacement) -
<code>aiosmtpd</code>. I tried both and ended up going with <code>aiosmtpd</code> because
interacting with it as a client was simple to do for humans.
<code>smtpd</code> required ending all commands with <code>&lt;CRLF&gt;.&lt;CRLF&gt;</code>, at least with the default
configuration. That is a tricky sequence to type for humans.</p>
<p><code>aiosmtpd</code> though, was just perfect! This is all that needed to be done:</p>
<pre><code>pip3 install aiosmtpd
aiosmtpd -n
</code></pre>
<p>That's it! An SMTP server is now running on <code>localhost:8025</code> and it will print any received emails to console.
That's brilliant if working locally, the default is exactly what we need.
No need for more code or monkeypatching, just some configuration tweaks.</p>
<p>You can even <a href="https://linux.die.net/man/1/nc"><code>netcat</code></a> to this port and try sending emails with raw SMTP
commands. It can be quite satisfying
<a href="https://twitter.com/n1kochiko/status/1460821886925901827">from my own experience</a>.</p>
<p>Besides ease of interacting for human clients, there are some more reasons to prefer <code>aiosmtpd</code>
over <code>smtpd</code> (in case you have to choose for your own needs):</p>
<ol>
<li>Extending it is very simple, <a href="#extending-aiosmtpd">as we will see</a>.
</li>
<li>Its default invocation (with only one CLI flag) starts the debugging server that
we want. No option values to remember.
</li>
<li><code>smtpd</code> is deprecated and will be removed from the standard library in Python3.12.
</li>
</ol>
<h1>Extending <code>aiosmtpd</code></h1>
<p>If local setup is all you wanted to know about, you can close the blog at this point. But I'll go
on and share how we solved another interesting problem -- verifying that code written by our students
was correct and that emails were actually being sent.</p>
<p>Now the caveat is that we were deploying our students' code to a website of their own [[2]]({{&lt; relref &quot;#references&quot; &gt;}})
and the checks should use HTTP endpoints on those websites for checking results. Because we had our
own deployment we had the luxury to be able to override certain environment variables on a per-request
basis without making changes to the application code (CGI is powerful!). This meant we could
use a different SMTP configuration when certain HTTP headers were received.</p>
<p>With that being sorted, I could write an extension class in less than 50 lines of code that
writes the email content to a single
file. We could then read from the same file to get the latest email. At our scale this was
good enough. I didn't need to worry about 10 people simultaneously submitting and overwriting
the files for each other, but if that were a concern for you, the
<a href="https://aiosmtpd.readthedocs.io/en/latest/handlers.html#aiosmtpd.handlers.Mailbox"><code>Maildir</code></a>
convention can be used instead.</p>
<p>This is the code for the extension, with comments for clarity:</p>
<pre><code class="language-python"># email package is part of the Python standard library,
# which provides an &quot;email.message.Message&quot;, along with
# parsers/generators for converting to/from a standard format.
from email.generator import Generator
from email.parser import Parser

# Message class handles all SMTP commands with sensible defaults
# and allows us to only implement a `handle_message` method
# that will be called with a parsed email message
from aiosmtpd.handlers import Message


class MailFile(Message):
    def __init__(self, mail_file, message_class=None):
        # we are taking a mail_file, the path that should be
        # mentioned on the CLI, that we write the mail to
        self.mail_file = mail_file
        super().__init__(message_class=None)

    def handle_message(self, message):
        # overwrite the old file
        with open(self.mail_file, &quot;w&quot;) as f:
            # Generator is an abstraction for parsing/writing the
            # email content and the headers
            g = Generator(f, mangle_from_=True, maxheaderlen=120)
            g.flatten(message)

    def get_latest_message(mail_file):
        &quot;&quot;&quot;Returns an email.message.Message object

        Useful accessors on Message object:
        `message[&quot;X-MailFrom&quot;]`
        `message[&quot;X-RcptTo&quot;]`
        `message.get_payload()` -&gt; str | list[str]
        `message.is_multipart()` -&gt; bool
        &quot;&quot;&quot;
        p = Parser()
        with open(mail_file) as f:
            return p.parse(f)

    @classmethod
    def from_cli(cls, parser, *args):
        # this gets the args received on command line
        if len(args) &lt; 1:
            parser.error(&quot;The file for the mailfile is required&quot;)
        elif len(args) &gt; 1:
            parser.error(&quot;Too many arguments for Mailbox handler&quot;)
        return cls(args[0])
</code></pre>
<p>Then if we package and pip install it as <code>mailcatcher</code>, we can write:</p>
<pre><code class="language-shell">aiosmtpd -n -c mailcatcher.catcher.MailFile latest.mail
</code></pre>
<p>The reference for extending should be the <a href="https://github.com/aio-libs/aiosmtpd/blob/master/aiosmtpd/handlers.py"><code>handlers.py</code></a>
file and the <a href="https://aiosmtpd.readthedocs.io/en/latest/handlers.html">documentation</a>.</p>
<p>This is the <a href="https://gist.github.com/nikochiko/93650f67235d93b8a35e090a5dcc5fed"><code>gist</code></a></p>
<h1>Unit tests with <code>aiosmtpd</code></h1>
<p>The same class that we wrote can be used for unit testing as well. Except in this case, you might not want to
have a long-running process for the SMTP server, and rather start/stop it for each test.</p>
<p>Here's how you can extend the same mailcatcher to build a pytest fixture which does that:</p>
<pre><code class="language-python">import pytest
from aiosmtpd.controller import Controller
from mailcatcher import MailFile

from app import config, function_that_should_send_email


@pytest.fixture
def mailcatcher(tmp_path):
    &quot;&quot;&quot;Fixture to setup a temporary email server

    `tmp_path` is provided by pytest.
    &quot;&quot;&quot;
    mail_file = tmp_path / &quot;testing.mail&quot;
    catcher = MailFile(mail_file)
    # you can set a different hostname/port as args to `Controller`
    controller = Controller(catcher)
    controller.start()
    config.update(smtp_hostname=&quot;localhost&quot;, smtp_port=&quot;8025&quot;,
                  smtp_username=&quot;&quot;, smtp_password=&quot;&quot;)
    yield catcher
    controller.stop()


def test_email_is_sent(mailcatcher):
    address = &quot;eva.lu.ator@example.com&quot;

    # procedure to be tested
    function_that_should_send_email(address)

    latest_email = mailcatcher.get_latest_email()
    assert latest_email is not None
    assert latest_email[&quot;X-RcptTo&quot;] == address  # or a loose check like `address in latest_email[&quot;X-RcptTo&quot;]`
    assert &quot;expected content&quot; in latest_email.get_payload()
</code></pre>
<p>I should mention that <a href="https://pythonhosted.org/lazr.smtptest/lazr/smtptest/docs/usage.html"><code>lazr.smtptest</code></a>
is also a good alternative with a similar API and workings, made especially for testing.</p>
<hr />
<p>Thanks for reading!
If you liked it or have some feedback, let me know on <a href="https://twitter.com/n1kochiko">Twitter</a>
or <a href="https://t.me/nikochiko">Telegram</a>.</p>
<h2>References</h2>
<p><strong>[1]</strong> <em>The training was delivered by <a href="https://pipal.in">Pipal Academy</a>. The guided project in question is open source: <a href="https://github.com/pipalacademy/rajdhani">repo link</a>. This repo has the skeleton needed, and the tasks are mentioned on the <a href="https://rajdhani.pipal.in">dashboard</a>. If you want to host this project and run in your own group, the code for dashboard site is available here: <a href="https://github.com/pipalacademy/rajdhani-challenge">repo link</a>. Let us know and we will help you do it.</em></p>
<p><strong>[2]</strong> <em>We built our own deployment platform for this, called <a href="https://github.com/pipalacademy/hamr">&quot;Hamr&quot;</a>. We satirically call it the &quot;next-gen&quot; serverless platform. It is made to be as simple and boring as possible -- using CGI to make the apps serverless and deployment script being a human-readable cURL without an elaborate JSON body. There were philosophical decisions that went into its design and it deserves a blog post of its own.</em></p>
]]></description>
      <pubDate>2022-09-21T17:15:43+05:30</pubDate>
      <category>tech</category>
      <category>python</category>
    </item>
    <item>
      <title>Writing an SMTP Server from Scratch (WIP)</title>
      <link>/posts/writing-an-smtp-server/</link>
      <guid>/posts/writing-an-smtp-server/</guid>
      <description><![CDATA[<p>A few months ago, I wrote an SMTP server from scratch to learn about the protocol. It soon came to me that the knowledge of
SMTP internals is not as common as it should be. Upon not finding a satisfactory blog on the topic, I read through the RFCs
and tried to create my own server. I am writing this blog as a relatively concise summary of that.</p>
<p>Before we go on to writing our server, I want to give some context that should be treated as a prerequisite.</p>
<p>SMTP is the protocol that is used to send and receive emails (as the name suggest, Simple Mail <em>Transfer</em> Protocol). When a
mail is sent by a user, it is handed over to an SMTP server to pass it on to its destination, which is a receiving SMTP
server. Once the mail reaches its destination SMTP server, other protocols such as IMAP or POP3 take over and store the
emails so that they can be retrieved by the receiving user at a later point.</p>
<p>SMTP uses stateful, long-lasting TCP connections where the client and server send data back and forth.
The client can even send multiple emails on the same connection. This is in contrast to HTTP 1, where client asks for
exactly one resource and the server sends back just that before the connection is closed.</p>
<p>The SMTP specification defines some commands that the client can use to set the headers and content. The client will send
these commands in their specified format with arguments, and the server will respond with a response code that the client
can understand. Optionally, the server can send some human-readable data that may be useful for debugging.</p>
<p>These are some of the basic commands that all SMTP servers should support:</p>
<ul>
<li><strong>HELO</strong>: Hello
<ul>
<li>Once the TCP connection is established, this is always the first command that the client should send. By this command,
the client identifies itself with a domain name (an FQDN - fully qualified domain name)
</li>
<li>Syntax: <code>HELO kausm.in</code>
</li>
</ul>
</li>
<li><strong>MAIL FROM</strong>: I want to send an email, and this email is from ...
<ul>
<li>This starts a mail transaction (<em>I want to send a mail</em>), and specifies the sender address (<em>and this email is from</em>).
</li>
<li>Syntax: <code>MAIL FROM: sender@kausm.in</code>
</li>
</ul>
</li>
<li><strong>RCPT TO</strong>: A recipient of this email is ...
<ul>
<li>This is used to specify a recipient of this mail. If a client wants to send a mail to multiple receiving addresses,
it should run this command multiple times with each of the receiving email addresses.
</li>
<li>Syntax: <code>RCPT TO: receiver@kausm.in</code>
</li>
</ul>
</li>
<li><strong>DATA</strong>: My mail data is ...
<ul>
<li>Starts the actual mail data. Once this command is run and an OK response is received from the server, the client should
go on to send first the email headers and the email content as text. The delimiter to end this text is <code>&lt;CRLF&gt;.&lt;CRLF&gt;</code>.
</li>
<li>Headers can be specified like <code>Date: Mon, 14 February 2022 20:40:34</code>, <code>Reply-To: kaustubh@kausm.in</code>, etc. separated by newlines
</li>
<li>Syntax:
<ul>
<li>Client: <code>DATA</code>
</li>
<li>Server: <code>250 OK</code>
</li>
<li>Client:
</li>
</ul>
<pre><code>Date: Mon, 14 February 2022 20:40:34
From: sender@kausm.in
To: receiver@kausm.in
Subject: Hello Receiver
Reply-To: kaustubh@kausm.in

Hey there

.

</code></pre>
</li>
</ul>
</li>
<li><strong>QUIT</strong>: That's all I wanted to do, thanks for your service.
<ul>
<li>This tells the server that its work is done and that the server can now close the connection.
</li>
<li>Note that the SMTP specification explicitly mentions that the server shoudln't close the connection abruptly unless the
client executes this command. Even after you complete a mail transaction, this gives you the option to send addiotional
mails over the same connection.
</li>
<li>Syntax: <code>QUIT</code>
</li>
</ul>
</li>
</ul>
<p>There are some more commands that can be used once the client and server agree that both support that command. For example,
the <code>LOGIN</code> command for authentication and <code>STARTTLS</code> for encryption. These are extensions that do not <em>have</em> to be implemented
by all SMTP servers, but are practically required to go past the security/spam filters of sophisticated receiving servers.
The functionality of these commands can be agreed upon by using an <code>EHLO</code> (<em>Extended Hello</em>) command instead of the usual
<code>HELO</code> command, which the server responds to with a list of supported extensions.</p>
<p><strong>OK, let's start.</strong></p>
<p>Here's what we need to do:</p>
<ol>
<li>Write a server that can accept and read from TCP connections.
</li>
<li>Support the <code>HELO</code> command.
</li>
<li>Support <code>MAIL FROM</code>.
</li>
<li>Support <code>RCPT TO</code>.
</li>
<li>Support <code>DATA</code>.
</li>
</ol>
<h4>1. Writing a TCP server</h4>
<pre><code class="language-go">1   package main
2   
3   import (
4-7      ...
8   )
9   
10  const HOST = &quot;localhost&quot;
11  const PORT = 3333
</code></pre>
<p>Initialize a listener on the host and port, and accept connections on it. See Go standard documentation of the
<a href="https://pkg.go.dev/net">net</a> pkg for more information.</p>
<pre><code class="language-go">13  func main() {
14  	// for now, we'll just listen on localhost:3333
15  	listenAddr := fmt.Sprintf(&quot;%s:%d&quot;, HOST, PORT)
16  
17  	listener, err := net.Listen(&quot;tcp&quot;, listenAddr)
18  	if err != nil {
19  		// for now, just panic with the error
20  		panic(err)
21  	}
22  
23  	// accept connections forever
24  	for {
25  		conn, err := listener.Accept()
26  		if err != nil {
27  			// simply log the error for now, and continue listening
28  			log.Printf(&quot;error while accepting conn: %v\n&quot;, err)
29  		} else {
30  			go handleConnection(conn)
31  		}
32  	}
33  }
</code></pre>
<p>In <code>handleConnection</code>, we'll first read from the connection into a buffer and then write that back into the connection.</p>
<pre><code class="language-go">35  func handleConnection(conn net.Conn) {
36  	defer func() {
37  		conn.Close()
38  	}()
39  
40  	// we'll be keeping the data here
41  	buffer := make([]byte, 1024)
42  
43  	// read into a buffer
44  	n, err := conn.Read(buffer)
45  	if err != nil {
46  		log.Printf(&quot;error while reading from connection: %v\n&quot;, err)
47  		return
48  	}
49  
50  	newlyRead := string(buffer[:n])
51      log.Printf(&quot;Got input from client: '%s'. Echoing it back now\n&quot;, newlyRead)
52  
53  	// now just echo it back with a prefix
54  	_, err = conn.Write([]byte(&quot;Server says: &quot; + newlyRead))
55  	if err != nil {
56  		log.Printf(&quot;error while writing to connection: %v\n&quot;, err)
57  		return
58  	}
59  }
</code></pre>
<p>Now we can test out the server with <a href="https://linuxize.com/post/netcat-nc-command-with-examples/">netcat</a>.</p>
<pre><code class="language-bash">$ # save the Go code in a main.go file and run this in one terminal
$ go run main.go

$ # in another terminal, start netcat and connect to our server
$ nc localhost 3333
Hello there. This will be echoed back
Server says: Hello there. This will be echoed back

</code></pre>
<p>Great, we now have our TCP listener. We can get started with the SMTP commands.</p>
<h4>2. Supporting SMTP commands</h4>
<p>To support different commands, we need to read and parse the line input by the client. Let's make a list of what we'll
need to do:</p>
<ul>
<li>Parse the text - split it into command and arguments
</li>
<li>If the command is a valid command, pass the <code>conn</code> object to its handler until it finishes its job and returns it
back.
</li>
<li>Sometimes, we need certain prerequisite data from commands before we can start executing other commands. For example,
the client has to send the <code>HELO</code>/<code>EHLO</code> command before being able to send any other command, the <code>MAIL FROM</code> needs to be
run before <code>RCPT TO</code> is run, and so on. For this reason, we have to keep state of what data we have received from the
client.
</li>
</ul>
<h5>The <code>HELO</code> command</h5>
<pre><code class="language-go">
</code></pre>
<hr />
<ol>
<li><em>RFCs: RFC 883, etc.</em>
</li>
</ol>
]]></description>
      <pubDate>2022-02-14T18:31:11+05:30</pubDate>
      <category>half-baked</category>
      <category>smtp</category>
      <category>tech</category>
    </item>
    <item>
      <title>Shivshankar Iyer S. - M. Kaustubh, Goa 2018</title>
      <link>/chess/goa-2018-round-10/</link>
      <guid>/chess/goa-2018-round-10/</guid>
      <description><![CDATA[<p>[Event &quot;Memorable Game: Shivshankar Iyer S - M. Kaustubh. Round 10, 1st Goa GM 2018 Cat B&quot;]
[Date &quot;2018.10.16&quot;]
[Result &quot;0-1&quot;]
[Variant &quot;Standard&quot;]
[ECO &quot;D32&quot;]
[Opening &quot;Tarrasch Defense: Symmetrical Variation&quot;]
[Annotator &quot;https://lichess.org/@/kaustico&quot;]
[Orientation &quot;black&quot;]</p>
<ol>
<li>d4 d5 2. c4 e6 3. Nf3 c5 4. e3 Nf6 5. Nc3 Nc6 6. cxd5 exd5 7. Be2 cxd4 8. Nxd4 Qb6!? 9. Na4 Qa5+ 10. Kf1?? Bd7 { [%csl Ra4][%cal Gc6d4,Rd7a4] } 11. Nc3 Bb4 12. Nb3 Qb6 { Sacrificing another pawn! However, White's king is uncastled and very importantly, his c1-bishop and the a1-rook are out of the game. If he takes the d5-pawn, he will have to face the consequences of lack of development, king in center, and open files leading straight to the king. } 13. Nxd5 Nxd5 14. Qxd5 O-O-O! { Dares white to take a 3rd pawn on f7. } 15. Qc4 Kb8 16. Qf4+ Ka8! { This was my idea anyway with Kb8. To tuck the king in the corner so that I can carry on with my attack without worrying. } 17. e4 { White's idea is to get the Bishop out to e3. Once that is done, the Rook covers the back rank. He can go g3-Kg2, or Kg1-h3-Kh2 to complete his development. If I wouldn't have found the next move, I would be down 2 pawns with no compensation, and would have eventually lost the game. } 17... Nd4!! { This is the only move to keep the advantage. The trick here being - if White goes Be3, I can go Nxe2 threatening his queen. If he takes on b6 and I take on f4, I have a nice idea that gives me at least a draw but probably more.
Another reason I was excited about this is because at this point I was seeing lines that were quite similar to the Game of the Century played by Fischer. Where he sacrificed a queen to lead with some minor piece combinations with his 2 bishops and a knight, with discovered checks like these.
https://lichess.org/study/mcmGGgdB } 18. Nxd4 (18. Be3?? Nxe2 19. Bxb6 Nxf4 20. Bxd8 Bb5+ 21. Kg1 Ne2+ 22. Kf1 { [%csl Gd8][%cal Ge2g3,Gh8d8] } 22... Rxd8 { There is no way to stop Ng3+, picking up an exchange. If White goes a4, I can simply drop the Bishop back to a6. Another cheeky threat is ...Nc1+, Kb1, Rd1#! }) 18... Qxd4 19. e5?? { White resigned after my next move. Can you find it? } 19... Qd1+!! { Taking advantage of White's backrank weakness, and my well positioned Bishops.
White resigned here. 0-1 } 20. Bxd1 Bb5+ 21. Be2 Rd1# *
</li>
</ol>
]]></description>
      <pubDate>2018-10-16T00:00:00+05:30</pubDate>
    </item>
    <item>
      <title>Kaustubh M.</title>
      <link>/</link>
      <guid>/</guid>
      <description><![CDATA[<h1>About me</h1>
<p><img
src="/photos/whoami-k.jpg"
style="height: 300px; width: auto;">
</img></p>
<p>Hello!<br />
I am kaustubh, erstwhile known (sometimes even fondly) as
<code>nikochiko</code>, <em>kubasth</em>, bubbles, or tubhtubh.</p>
<p>I write computer programs at my job and sometimes for fun.
I take an interest in (non-exhaustively):</p>
<ul>
<li>Free and Open Source Software (FOSS)
</li>
<li>Self Hosting / Tech Independence
</li>
<li>Functional Programming &amp; Making of Programming Languages
</li>
<li>Chess
</li>
<li>Tech Policy &amp; Politics
</li>
</ul>
<p>If you are here, chances are that I'll love talking to you.
I believe I am easy to nerdsnipe and it would make me happy
to receive an email about something that fascinated you
recently.</p>
]]></description>
    </item>

  </channel>
</rss>
