<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: druchan</title>
    <description>The latest articles on Forem by druchan (@druchan).</description>
    <link>https://forem.com/druchan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1022498%2Fc1b68654-57e0-4417-9a23-e531642b01b9.png</url>
      <title>Forem: druchan</title>
      <link>https://forem.com/druchan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/druchan"/>
    <language>en</language>
    <item>
      <title>500 days of rdigest</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Sat, 07 Mar 2026 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/500-days-of-rdigest-4hgl</link>
      <guid>https://forem.com/druchan/500-days-of-rdigest-4hgl</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/chandru89new/rdigest" rel="noopener noreferrer"&gt;&lt;code&gt;rdigest&lt;/code&gt;&lt;/a&gt; is a small CLI-based tool I made in late 2024. It began as a way to manage my daily reading (online digital consumption) because YouTube's UI was distracting, RSS feed readers and their infinitely-scrolling pages were overwhelming and I had cut out social media from life eons ago. TL;DR, digital content consumption was at some unmanageable crossroads in life and I had to find a way to deal with it.&lt;/p&gt;

&lt;p&gt;I've been using &lt;code&gt;rdigest&lt;/code&gt; for 500+ days now. Of the handful of side-projects I built to scratch my own itch, this remains a powerful daily-driver.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;h6&gt;
  
  
  Contents
&lt;/h6&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="//#i."&gt;A quick brief of what &lt;code&gt;rdigest&lt;/code&gt; is&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//#ii."&gt;Learning by building&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//#iii."&gt;Hand-crafting software and LLMs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//#iv."&gt;Scratching an itch vs. building for others&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//#v."&gt;Sporadicity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#i."&gt;I.&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;Now, this part about what &lt;code&gt;rdigest&lt;/code&gt; is (and why) is for the accidental reader who landed here god knows how:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rdigest&lt;/code&gt; is technically a feed reader but it's also not really a feed reader. It collects links from all the feeds you follow and then creates a daily digest for you. The daily digest is just a finite bunch of links. A bunch of links grouped by source and zero algorithm. A bunch of links you look at, skim through, click on the ones you want to actually read or view... and then you're done with your digital content catch up!&lt;/p&gt;

&lt;p&gt;That last bit is the critical piece, the linchpin ideology.&lt;/p&gt;

&lt;p&gt;The way we consume digital content today is horrible. There's an infinite, never-ending stream hitting at us at all times. All kinds of platforms have interfaces that are designed cunningly to trick your brain into staying on the platform for as long as possible. You're thinking about Twitter, Instagram, TikTok and YouTube, but even other outlets like blogging engines, content management systems, AT-Proto interfaces (Mastodon/Bluesky) and even popular feed readers promote and enable these "infinite stream" interfaces.&lt;/p&gt;

&lt;p&gt;This leads to all kinds of problems. Attention is low, ability to sustain a longform is gone, and no interface or product gives you this feeling of closure. The same mechanics of constant connectivity over work Slack ruining work-life boundaries are at play: it never feels like you're done reading, seeing, hearing the things you want to for a given day.&lt;/p&gt;

&lt;p&gt;Alright, so that's the tirade against dark-pattern-embracing products streaming endless content and hacking into our neurons.&lt;/p&gt;

&lt;p&gt;There may be many antidotes to this. &lt;code&gt;rdigest&lt;/code&gt;'s idea is borrowed from the old, physical newspaper model that I &lt;a href="https://notes.druchan.com/escape-from-algotraz#v.-%22done%22-for-the-day" rel="noopener noreferrer"&gt;harp about&lt;/a&gt;. Imagine the morning newspaper (ie, if you're old enough). The paper arrives. You skim through the headlines, read the ones that interest you, and then you're done for the day. There's nothing more to do there (unless you were some hotshot that got the &lt;em&gt;evening&lt;/em&gt; paper too). &lt;code&gt;rdigest&lt;/code&gt; pretty much does the same, except it is about links from RSS feeds you follow and it's got no frills. Just a page of links like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="/images/rdigest.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijb95exqyi5r5onz6uag.png" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#ii."&gt;II.&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;Reminiscing about a "hand-written" side-project in the age of LLM-written software seems weird and futile. There's nothing special anymore about building software except for the big picture bits and the devil-is-in-the-details bits.&lt;/p&gt;

&lt;p&gt;And yet, artisanal software, especially of the crude, &lt;a href="https://www.robinsloan.com/notes/home-cooked-app/" rel="noopener noreferrer"&gt;home-cooked&lt;/a&gt; kind might retain its charm.&lt;/p&gt;

&lt;p&gt;I had two hopes when I began &lt;code&gt;rdigest&lt;/code&gt;. One, to build something that eventually helps me manage my daily digital consumption (that had spiralled out of control to the point of giving up on online reading). Two, to learn a programming language (Haskell in this case) along the way.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://notes.druchan.com/haskell-journal-1" rel="noopener noreferrer"&gt;kept a journal&lt;/a&gt; while I built &lt;code&gt;rdigest&lt;/code&gt;. The first entry goes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Decided to finally build something in Haskell to learn the language. So far, I've been solving puzzles and similar tasks, but nothing has given me the confidence to say, "Yes, I can build that in Haskell."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As I built out &lt;code&gt;rdigest&lt;/code&gt;, I ended up learning nuanced things about file I/O, batched concurrency, safely interacting with SQLite, implementing a small migration module, and more, all from within the Haskell ecosystem. Like all side-projects, it was fun, &lt;a href="https://notes.druchan.com/haskell-journal-1#day-12" rel="noopener noreferrer"&gt;it was frustrating at times&lt;/a&gt;, and thankfully it also ended up being rewarding.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#iii."&gt;III.&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;Work on &lt;code&gt;rdigest&lt;/code&gt; began in late 2024. By this time, I was using ChatGPT at work as an intelligent rubber-duck that talks back. Whatever I'd have googled or Stack Overflowed, I ChatGPT'd.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rdigest&lt;/code&gt; was built by hand but definitely helped by inputs from ChatGPT. Of course there were also many instances when the LLM sent me on wild goose-chases and proffered irrelevant complexities.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wrote some initial code based on ChatGPT's example of scraping. However, ChatGPT got the &lt;code&gt;attr&lt;/code&gt; function wrong.&lt;/p&gt;

&lt;p&gt;ChatGPT suggested &lt;code&gt;optparse-applicative&lt;/code&gt;, but I decided it was overkill for what I needed. A simple &lt;code&gt;--url &amp;lt;url&amp;gt;&lt;/code&gt; argument was enough.&lt;/p&gt;

&lt;p&gt;I had a long chat with ChatGPT about these things, asking it about the idea of bubbling up the errors from ExceptT without having to do &lt;code&gt;runExceptT&lt;/code&gt; wherever I wanted the error to be bubbled up but the ideas it returned were not working or not useful — or in some cases, it was just reinventing the ExceptT or ReaderT monad transformers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But also:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;... I learned how to bundle the template as part of the binary by inlining/embedding it using the &lt;code&gt;file-embed&lt;/code&gt; package—all of this thanks to ChatGPT.&lt;/p&gt;

&lt;p&gt;Asked ChatGPT for a good web scraping library in Haskell. It suggested &lt;code&gt;tagsoup&lt;/code&gt; and &lt;code&gt;html-conduit&lt;/code&gt; (for fetching the source). Took a look at the examples and docs... seemed a little hard to grasp. Asked ChatGPT for a simpler alternative, and it suggested &lt;code&gt;scalpel&lt;/code&gt;. Liked it, and decided to use it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude had not yet come up as a significant competitor. Today, I don't use ChatGPT almost ever.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#iv."&gt;IV.&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;For a very long time — ie, over a year — &lt;code&gt;rdigest&lt;/code&gt; remained a CLI-only tool. The only way to add feeds, manage them, produce a daily digest, and so on was to use the tool in a terminal.&lt;/p&gt;

&lt;p&gt;But right there on my &lt;a href="https://notes.druchan.com/haskell-journal-1#day-1" rel="noopener noreferrer"&gt;day 1 journal entry&lt;/a&gt;, I had wanted to also make &lt;code&gt;rdigest&lt;/code&gt; spin up a local server so that the feeds could be managed and daily digests could be viewed on a browser.&lt;/p&gt;

&lt;p&gt;Life got in the way. I would occasionally push an update or two. I even built a now-defunct feature that sent the daily digest links via Telegram. But the server feature never happened.&lt;/p&gt;

&lt;p&gt;I finally managed to find some motivation to build out this feature in the last week. What's special about this is the stack: the heavy-lifting backend, server and CLI are all in &lt;a href="https://www.haskell.org/" rel="noopener noreferrer"&gt;Haskell&lt;/a&gt;, a pure functional language, and the frontend application that renders a near-brutalist, minimal web interface is written in &lt;a href="https://elm-lang.org/" rel="noopener noreferrer"&gt;Elm&lt;/a&gt;, a pure functional language inspired by Haskell. There was a brief moment where I considered &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;htmx&lt;/a&gt;, but chose Elm eventually.&lt;/p&gt;

&lt;p&gt;The local server feature is not something I use every day. I have &lt;a href="https://github.com/chandru89new/rdigest-data/blob/main/.github/workflows/refresh.yml" rel="noopener noreferrer"&gt;a different setup&lt;/a&gt; to make &lt;a href="https://chandru89new.github.io/rdigest-data/" rel="noopener noreferrer"&gt;the daily digests available "on the cloud"&lt;/a&gt; so I can view them no matter where I am, even if I don't have my laptop. In retrospect, maybe this was the friction that kept me from building the local-server feature; I didnt really need it.&lt;/p&gt;

&lt;p&gt;But, like my other project &lt;a href="https://vaak.druchan.com/" rel="noopener noreferrer"&gt;vāk&lt;/a&gt; where I keep wobbling between "make this usable for everyone" and "just build things for myself", the thrust for &lt;code&gt;rdigest&lt;/code&gt;'s local-server feature comes from wanting to make this tool easier to use for others as well. You know, just in case. The odds of someone using it are lower than the odds of a comet striking our planet in the next fifty years. But just in case.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#v."&gt;V.&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;Work on &lt;code&gt;rdigest&lt;/code&gt; happens sporadically. The &lt;a href="https://github.com/chandru89new/rdigest/commits/main" rel="noopener noreferrer"&gt;commit history&lt;/a&gt; says I worked on it for a few concerted days in Jan 2025, then March 2025, then a big sabbatical till November 2025 and then now in March 2026. Big periods of inactivity, small chunks of moderately intense activity. Basically, the tale of every other side-project that gets used routinely.&lt;/p&gt;

&lt;p&gt;This pattern repeats itself in &lt;code&gt;vāk&lt;/code&gt; too, and then also on my toy npx-powered game &lt;a href="https://github.com/chandru89new/lvnshtn/" rel="noopener noreferrer"&gt;&lt;code&gt;wordladder&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's worth talking about because going back to these projects after a long hiatus does not involve broken builds or security conflagration or fuzzy/messy recollection of the modules/functions... you know, the kind of things you'd expect in projects built in weak languages unfortunately powering the world.&lt;/p&gt;

&lt;p&gt;Instead, there's clarity, assurance, and guarantee. I attribute most of this to the semantics of the languages. &lt;code&gt;vāk&lt;/code&gt; and &lt;code&gt;wordladder&lt;/code&gt; use PureScript (a Haskell-inspired language targeting Node/JS environments). &lt;code&gt;rdigest&lt;/code&gt; uses Haskell and Elm. Changes and even massive refactors are not scary.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;So yeah. 500 days of &lt;code&gt;rdigest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I know. All you've been thinking of is, "the project needs a better name."&lt;/p&gt;

</description>
      <category>functional</category>
      <category>haskell</category>
      <category>rss</category>
      <category>project</category>
    </item>
    <item>
      <title>Going back to the morning newspaper model</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Mon, 02 Mar 2026 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/going-back-to-the-morning-newspaper-model-41k</link>
      <guid>https://forem.com/druchan/going-back-to-the-morning-newspaper-model-41k</guid>
      <description>&lt;p&gt;Recently, &lt;a href="https://www.terrygodier.com/current" rel="noopener noreferrer"&gt;Current RSS reader&lt;/a&gt; hit the headlines and I had a gander at what it is, how it's designed and what the underlying philosophy is.&lt;/p&gt;

&lt;p&gt;It's a beautiful-looking app. But the fundamentals are problematic and repeat the same mistakes that lead to different kinds of problems you and I face w.r.t. digital content overwhelm as we try to grapple with, catch up on, and manage our digital consumption.&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#i.-we-consume-about-a-fraction-of-what-hits-our-streams"&gt;I. We consume about a fraction of what hits our streams&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;For many years now &lt;a href="https://en.wikipedia.org/wiki/RSS" rel="noopener noreferrer"&gt;RSS feeds&lt;/a&gt; have been a boon as social media and YouTube took over the digital content landscape. But as the maker of the Current RSS reader points out, the email inbox-style design has added a subconscious anxiety to managing RSS feeds too. Why do we need an unread count?&lt;/p&gt;

&lt;p&gt;When you are subscribed to a few hundred feeds, the amount of links staring at you is huge. But, and here's the obvious thing, we actually are interested in, on a rough average, about 20% of things out of that list. It makes no sense to have an anxiety-inducing user-interface that says you have 1438 unread links. You probably care only to read about 20-30 of them today.&lt;/p&gt;

&lt;p&gt;But this is not the only problem. This is not even the key problem.&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#ii.-%22streams%22-are-problematic%2C-and-they-are-using-it-to-hack-our-behavior."&gt;II. "Streams" are problematic, and they are using it to hack our behavior.&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;In my W.I.P. guide/booklet, &lt;a href="https://notes.druchan.com/escape-from-algotraz#iii.-stream-vs-batch" rel="noopener noreferrer"&gt;&lt;em&gt;Escape from Algotraz&lt;/em&gt;&lt;/a&gt;, I talk about this fundamental issue about social media "feeds". They are an &lt;em&gt;infinite stream&lt;/em&gt;. Never-ending. The more you scroll, the more it keeps scrolling.&lt;/p&gt;

&lt;p&gt;This is one of the darkest design patterns we've cooked up. If you grew up in the older internet, you remember hitting the end of a paginated list of items. There used to be an &lt;em&gt;end&lt;/em&gt; to things. Now there isn't. It's not an eventuality.&lt;/p&gt;

&lt;p&gt;Despite there being an infinite amount of content to consume, apps can decide to "end" the list of items for you. They can choose to say, "oh that's it for now, come back and check in a few hours, we'll show you new content." But they won't.&lt;/p&gt;

&lt;p&gt;RSS feeds are limited by the number of feeds you're subscribed to but they still manage to &lt;em&gt;stream&lt;/em&gt; an almost infinite-looking stream at you. Couple that with the unread count and you're back to anxiety-town about unmanageable digital content consumption.&lt;/p&gt;

&lt;p&gt;Apps like Current RSS reader (which literally uses the word "river" for the stream of content from your feeds) are merely reinforcing this bad pattern. Smart removal of items from the feed is not going to solve the fundamental issue (and it only introduces more cognitive load for us).&lt;/p&gt;

&lt;p&gt;We do not want streams. Our brains are not wired to process streams. They need a break and, if the designers and developers care, apps can be designed this other way.&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#iii.-our-brains-are-good-with-batch-processing."&gt;III. Our brains are good with batch processing.&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;Have you ever done this thing where you said to yourself that you'll catch up on the unread items over the weekend? Or that you'll do all the chores (cleaning, laundry, meal prep) over the weekend?&lt;/p&gt;

&lt;p&gt;We're essentially "batching". Turns out batching is a good antidote to "streams".&lt;/p&gt;

&lt;p&gt;Here's another analogy. This one works for folks who grew up with physical newspapers around. Do you remember getting the morning newspaper, skimming through the headlines and reading a few of the news items in detail? What after that? We were done. That's it. Even if you went back to read some more, there was this palpable feeling of being "done". A closure of sorts. Task = done.&lt;/p&gt;

&lt;p&gt;The digital user interface landscape today is designed specifically to prevent you from feeling this way.&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#iv.-the-onus-is-on-ourselves-to-fix-this-for-us."&gt;IV. The onus is on ourselves to fix this for us.&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;I couldn't find an app that did this for me and worse, I couldn't find literature around this either. While there's so much brouhaha about &lt;a href="https://calmtech.com/" rel="noopener noreferrer"&gt;calm technology&lt;/a&gt;, when it comes to content overwhelm, it seems the onus is on us, the users, to find out a way to fix it.&lt;/p&gt;

&lt;p&gt;So I started experimenting with ways to manage my content (and discovering, inculcating and testing the underlying philosophical argument). Eventually, I managed to write up a simple solution: every day, I produce a plain HTML page of links from all my feed sources. With my 300+ feed subscriptions, I get about 400 links on average. Of this, I am only interested in about 10-20% on average.&lt;/p&gt;

&lt;p&gt;This plain HTML page is my newspaper equivalent. While I do read news elsewhere, this "digest" happens to be my way of batch processing my daily content I want to keep tabs on.&lt;/p&gt;

&lt;p&gt;And it has worked fabulously well for about 500 days now. In the morning, I open my &lt;em&gt;digest&lt;/em&gt;, and open the links that I am interested in (a combination of news items, op-eds, LettersOfNote-like pieces, stuff from Hacker News/Lobsters, Veritasium-like YouTube videos, some cooking channels, some chess channels, parody or satirical content etc.). And then, if I have time in the morning, finish reading most of it before work, or read them in bits and pieces through the day.&lt;/p&gt;

&lt;p&gt;Most importantly, I know this is all I want to read for the day. There may be an occasional 2-3 extra things I consume (someone shared a link on WhatsApp; I accidentally opened social media; links from email).&lt;/p&gt;

&lt;p&gt;At the basic level, why this has worked well for me is the mental perception. My digest gives me a finite list of content to work through. Out of this finite list, I am interested in a fraction. It's all ridiculously manageable.&lt;/p&gt;

&lt;p&gt;The fact that we're only interested in a small fraction of content should be so obvious, right? But try that with an infinite stream of content. A fraction of infinity still feels like infinity. But with finite content — as in a digest or a newspaper — you realise the manageability instantly.&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="//#v.-%22how-we-got-here%22-is-clear.-and-so-is-%22how-to-get-out-of-here%3F%22"&gt;V. "How we got here" is clear. And so is "how to get out of here?"&lt;/a&gt;
&lt;/h6&gt;

&lt;p&gt;I am convinced that an entire class of problems is solved if the interface lends itself to reminding us of the finiteness of content and time. (Look no further than the "inbox zero" trend. The idea was to create finite chunks out of an infinite stream, so that you can then manage that finite chunk easily while also having the (false) feeling of having cleared everything.)&lt;/p&gt;

&lt;p&gt;Eschew streams.&lt;/p&gt;

&lt;p&gt;Create interfaces (or pick those) that have a finiteness to them. No infinitely scrolling interfaces. No constantly &lt;em&gt;streaming&lt;/em&gt; screens. Just simple plain pages that start and end and when they end, it's a nice little reminder that you are done for the day and can go back to other things in life.&lt;/p&gt;

&lt;p&gt;If we want to go &lt;em&gt;back&lt;/em&gt; to a healthier digital content consumption era, we might just need to use the principles of older interfaces going back to the newspaper era.&lt;/p&gt;

&lt;p&gt;P.S.: I didn't want to make this a plug for my solution so I am linking to the open-source codebase here: &lt;a href="https://github.com/chandru89new/rdigest" rel="noopener noreferrer"&gt;rdigest&lt;/a&gt;. The app doesn't matter; what matters is the underlying principle of design that I've written about above and that's something almost anyone can now mimic with the advent of AI/LLMs.&lt;/p&gt;

</description>
      <category>rss</category>
      <category>feedreader</category>
      <category>current</category>
    </item>
    <item>
      <title>One year of vāk</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Sat, 07 Feb 2026 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/one-year-of-vak-43ba</link>
      <guid>https://forem.com/druchan/one-year-of-vak-43ba</guid>
      <description>&lt;p&gt;Some time back, in a routine cleanup of my many drafts, I found one where I "introduced" &lt;a href="https://github.com/chandru89new/vaak" rel="noopener noreferrer"&gt;vāk&lt;/a&gt;, the blogging script/tool I use to run my blog. I never quite finished that draft. I am certain I must've dissuaded myself from writing it by thinking, "no one's going to read it anyway."&lt;/p&gt;

&lt;p&gt;About a couple of days back, I introduced a new feature to vāk: collections. Collections is just "tags" in some other shape. Testing this new feature is how I stumbled on the old, unfinished draft introducing my custom blogging tool.&lt;/p&gt;

&lt;p&gt;This then led to checking the earliest commit in vāk's repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit a19750ed19c1e4fd684092b7a7be6b263a3d7038
Author: Chandru &amp;lt;...&amp;gt;
Date: Fri Feb 7 18:49:37 2025 +0530

    init commit

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is about a year ago. Calls for some kind of a small celebration.&lt;/p&gt;

&lt;p&gt;But in truth, vāk predates this timeline. The contents of my blog and the tool existed in the same repo. Feb 7, 2025 is the date I separated the tool (vāk) from the blog (this one) and initialised a new repo for the tool. Checking the earliest commit on the blog itself, back when vāk was still a part of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 244434366b7f5b11c0850041b52c50345e447ce9
Author: Chandru &amp;lt;...&amp;gt;
Date: Fri May 12 16:23:53 2023 +0530

    Init commit.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's almost 3 years ago.&lt;/p&gt;

&lt;p&gt;In keeping with the mores of my time in the 2000s, I set up my first blog on Blogger.com. I might've set up a few in haste and deleted all of them (in haste too). When I had much to write about, like the journals during my first stint in Ahmedabad, I set up a blog on Tumblr. Then, back in Chennai, I set up a new one, on WordPress this time. This was also the time when I attended a blogger's meet conducted by &lt;a href="https://www.indiblogger.in/" rel="noopener noreferrer"&gt;IndiBlogger&lt;/a&gt;. At approximately 4-5 posts a month on average, I published about 50 posts on the WordPress blog before retiring it (I have no idea why) and resuming my writing on Tumblr.&lt;/p&gt;

&lt;p&gt;Tumblr was a go-to whenever I needed a blog or a website to put something up. I had gotten used to its theming (which was just CSS wrangling). It was just way too easy to set up and run.&lt;/p&gt;

&lt;p&gt;Until finally, I decided that as a programmer, it would be nice to have a blogging tool built by hand. Might've been the &lt;a href="https://dev.to/prog-rites-of-passage"&gt;rite of passage&lt;/a&gt; thing.&lt;/p&gt;

&lt;p&gt;Around this time, I was trying to learn Haskell and PureScript. My blog posts were going to be simple Markdown files and I knew it was trivial to convert Markdown content into HTML content using JS libraries. PureScript offers a really simple and easy &lt;a href="https://book.purescript.org/chapter10.html" rel="noopener noreferrer"&gt;FFI&lt;/a&gt; so I started building out vāk in PureScript.&lt;/p&gt;

&lt;p&gt;Those initial commits (3 years ago) started as a &lt;code&gt;.purs&lt;/code&gt; script that fetched all my posts from Tumblr (via an API) and rewrote them as Markdown files, because at the time, Tumblr's API returned an HTML version of the posts.&lt;/p&gt;

&lt;p&gt;Then, I wrote a bunch of functions that generally converted a given Markdown file into an HTML file. The reason for this roundabout was because future posts were going to be written in Markdown.&lt;/p&gt;

&lt;p&gt;A few iterations later, broad contours of the system were ready. I had one repo to rule them all: the blog, the content, the published website etc.&lt;/p&gt;

&lt;p&gt;About a year ago, I split the tool into it's own repo and called it vāk. (&lt;a href="https://github.com/chandru89new/vaak?tab=readme-ov-file#colophon" rel="noopener noreferrer"&gt;Why vāk?&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In the years since, there have been only two major upgrades to vāk besides the usual and infrequent tweaks and clean-ups.&lt;/p&gt;

&lt;p&gt;The first is an internal detail. I used something called &lt;a href="https://en.wikipedia.org/wiki/Monad_transformer" rel="noopener noreferrer"&gt;monad transformers&lt;/a&gt; to create better abstractions to run functions and catch errors.&lt;/p&gt;

&lt;p&gt;The second is making the templates very customisable by relying on the &lt;a href="https://mozilla.github.io/nunjucks/" rel="noopener noreferrer"&gt;Nunjucks&lt;/a&gt; templating engine. This came about as a continuation of my unnecessary effort to make vāk a tool anyone can use.&lt;/p&gt;

&lt;p&gt;In a strongly-typed functional language, changes are a delight to make. I often employ this idea called type-driven design; it's like sketching out the app (or feature) on a wireframe of types before implementing the functions that do the thing.&lt;/p&gt;

&lt;p&gt;I picked up a few PureScript/Haskell skills along the way. Prior to working on vāk, I kept away from monad transformers and hadn't really worked with concurrency. I hadn't architected anything larger than a script that solved LeetCode problems. Building vāk on PureScript gave me a chance to do all of that and more.&lt;/p&gt;

&lt;p&gt;In the last few weeks, when I updated the program to use Nunjucks, I used Claude Code as an assistant to implement many of the changes. PureScript is notoriously niche so LLMs do not always get things right as often as they do when writing Python or TypeScript. But they do get you close enough.&lt;/p&gt;

&lt;p&gt;I continue to like the satisfaction of writing things myself than relying on an agent (except for work where dictates of productivity and releasing a feature trump personal artisanal satisfaction), so I tend to &lt;em&gt;ideate&lt;/em&gt; with Claude Code and then implement things by hand. Features, bugs, optimisations: I follow the same simple rule.&lt;/p&gt;

&lt;p&gt;A few of the hobbyist programs I build get used often, fewer still give me an opportunity to tinker with for a long time. vāk happens to be one that allows for both. It's one of those &lt;a href="https://www.robinsloan.com/notes/home-cooked-app/" rel="noopener noreferrer"&gt;home-cooked software&lt;/a&gt; things that I keep trying to &lt;em&gt;generalise&lt;/em&gt; for no reason.&lt;/p&gt;

</description>
      <category>blogging</category>
      <category>functional</category>
      <category>purescript</category>
      <category>project</category>
    </item>
    <item>
      <title>Bringing 1600+ Lucide icons into Elm</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Wed, 04 Feb 2026 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/bringing-1600-lucide-icons-into-elm-1okb</link>
      <guid>https://forem.com/druchan/bringing-1600-lucide-icons-into-elm-1okb</guid>
      <description>&lt;p&gt;&lt;a href="https://lucide.dev" rel="noopener noreferrer"&gt;Lucide icons&lt;/a&gt; has some really wonderful icons for interface design and is one of the most popular icon sets today.&lt;/p&gt;

&lt;p&gt;In the past, for my Elm projects, I've used Feather icons. In new Elm projects, I wanted to use Lucide Icons, only to discover that there is no Elm package to import and use Lucide icons. I was surprised: the Elm ecosystem, even if niche, pretty much supports all modern / contemporary libraries and tools. So not finding an Elm Lucide package was very surprising.&lt;/p&gt;

&lt;p&gt;Just as surprisingly, the &lt;a href="https://github.com/feathericons/elm-feather" rel="noopener noreferrer"&gt;Feather icons Elm package&lt;/a&gt; hasn't been updated in 6+ years.&lt;/p&gt;

&lt;p&gt;And so, a new side-quest was born: publishing the first &lt;a href="https://package.elm-lang.org/packages/chandru89new/elm-lucide/latest/" rel="noopener noreferrer"&gt;elm-lucide&lt;/a&gt; package to bring Lucide icons to the Elm ecosystem.&lt;/p&gt;

&lt;p&gt;The first thing I did was to find out how Feather icons were being rendered in Elm. I found this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alignLeft : Icon
alignLeft =
    makeBuilder "align-left"
        [Svg.line [ x1 "17", y1 "10", x2 "3", y2 "10"] []
        , Svg.line [x1 "21", y1 "6", x2 "3", y2 "6"] []
        , Svg.line [x1 "21", y1 "14", x2 "3", y2 "14"] []
        , Svg.line [x1 "17", y1 "18", x2 "3", y2 "18"] []
        ]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, just use SVG module to render the paths and other tags. The values of these tags come from the SVG content for each icon.&lt;/p&gt;

&lt;p&gt;To do this for Lucide, I'll need two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;download all SVG data for all the 1600+ icons on Lucide&lt;/li&gt;
&lt;li&gt;then, for each SVG, convert the XML into Elm-syntax&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Getting all the Lucide icons is simple. &lt;a href="https://lucide.dev/guide/packages/lucide-static" rel="noopener noreferrer"&gt;lucide-static&lt;/a&gt; is a JavaScript file that has all the SVG icons. I ran a simple &lt;code&gt;curl&lt;/code&gt; to fetch all the icons in a single JS file.&lt;/p&gt;

&lt;p&gt;Each icon looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const TextAlignStart = `
&amp;lt;svg
  class="lucide lucide-text-align-start"
  xmlns="http://www.w3.org/2000/svg"
  width="24"
  height="24"
  viewBox="0 0 24 24"
  fill="none"
  stroke="currentColor"
  stroke-width="2"
  stroke-linecap="round"
  stroke-linejoin="round"
&amp;gt;
  &amp;lt;path d="M21 5H3" /&amp;gt;
  &amp;lt;path d="M15 12H3" /&amp;gt;
  &amp;lt;path d="M17 19H3" /&amp;gt;
&amp;lt;/svg&amp;gt;
`;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to take the contents inside the &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; tag, convert them into Elm-specific syntax. Basically:&lt;/p&gt;

&lt;p&gt;from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;path d="M21 5H3" /&amp;gt;
&amp;lt;path d="M15 12H3" /&amp;gt;
&amp;lt;path d="M17 19H3" /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S.path [SA.d "M21 5H3"] []
, S.path [SA.d "M15 12H3"] []
, S.path [SA.d "M17 19H3"] []

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While I pondered over this, I realized that if I could transform this SVG XML into an &lt;a href="https://en.wikipedia.org/wiki/Abstract_syntax_tree" rel="noopener noreferrer"&gt;AST&lt;/a&gt;, I can then parse it to extract the relevant info I need.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    children: [
        { node: "path", attributes: [{ d: "M21 5H3" }] },
        { node: "path", attributes: [{ id: "M15 12H3" }] },
        ...
    ]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can then use this structure and create a string which is valid Elm syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S.path [SA.d "M21 5H3"] []
, S.path [SA.d "M15 12H3"] []
, S.path [SA.d "M17 19H3"] []

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trick was that I needed to do this SVG/XML to AST conversion in a Node environment. Upon research, I found that &lt;a href="https://github.com/jsdom/jsdom" rel="noopener noreferrer"&gt;&lt;code&gt;jsdom&lt;/code&gt;&lt;/a&gt; helps with this. So, I ended up doing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try {
  let div = new jsdom.JSDOM(
    `&amp;lt;div&amp;gt;${svgString}&amp;lt;/div&amp;gt;`
  ).window.document.querySelector("div");
  let svg = div.querySelector("svg");
  icon.classList = svg.classList.toString();
  icon.children = [];
  let children = Array.from(div.querySelectorAll(`svg &amp;gt; *`));
  icon.children = children
    .map((c) =&amp;gt; [c.nodeName, extractAttributes(c.attributes)])
    .map(childToList);
  return icon;
} catch (e) {
  log(e);
  return null;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get the SVG string (this was easy, just read the value of an exported icon from lucide-static.js)&lt;/li&gt;
&lt;li&gt;wrap it in a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and make &lt;code&gt;jsdom&lt;/code&gt; render it&lt;/li&gt;
&lt;li&gt;then, extract what I need (&lt;code&gt;svg&lt;/code&gt;'s children)&lt;/li&gt;
&lt;li&gt;convert each child into a tuple of &lt;code&gt;[node name, node attributes as key-value pairs]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, for this node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;path d="M13 21h8" /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the tuple I get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;["path", { d: "M13 21h8" }];

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, once I have all the tuples, I just convert them into Elm strings.&lt;/p&gt;

&lt;p&gt;And I combine all of that into one large module-declaring string and then print that to an Elm file called &lt;code&gt;LucideIcons.elm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One additional trick I need to do here is to enable customization of the icon. So I construct the icon in a way where it's a function that takes an &lt;code&gt;options&lt;/code&gt; parameter (of type &lt;code&gt;List (Svg.Attribute msg)&lt;/code&gt;) and renders the SVG icon with those options. This allows for some nice, simple customization of the icon as an SVG element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alignLeftIcon : List (S.Attribute msg) -&amp;gt; H.Html msg
alignLeftIcon options =
    S.svg (baseOptions ++ options) [S.path [ SA.d "M21 5H3"] [], S.path [SA.d "M15 12H3"] [], S.path [SA.d "M17 19H3"] [] ]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of this is in a &lt;code&gt;build.js&lt;/code&gt; script that automates the whole thing. When I run this, I have a &lt;code&gt;LucideIcon.elm&lt;/code&gt; file which defines and exports all the 1600+ icons in Lucide as Elm functions.&lt;/p&gt;

&lt;p&gt;Since the &lt;code&gt;build.js&lt;/code&gt; script downloads the latest &lt;code&gt;lucide-static&lt;/code&gt;, whenever there is an update in Lucide's version, I just run this and I have all the new/updated icons as well.&lt;/p&gt;

&lt;p&gt;You can find the &lt;a href="https://package.elm-lang.org/packages/chandru89new/elm-lucide/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;elm-lucide&lt;/code&gt; package here&lt;/a&gt;. I try and update as soon as there are updates to the main Lucide library.&lt;/p&gt;

</description>
      <category>elm</category>
      <category>frontend</category>
      <category>showdev</category>
      <category>ui</category>
    </item>
    <item>
      <title>Functional programming in an LLM world</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Tue, 03 Feb 2026 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/functional-programming-in-an-llm-world-3mnc</link>
      <guid>https://forem.com/druchan/functional-programming-in-an-llm-world-3mnc</guid>
      <description>&lt;p&gt;A twenty-something and their ChatGPT has produced a code artefact that is currently in production. Somewhere deep in the code soup, there is this utility function...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function calculateShipping(weight, distance, isExpress) {
  const baseRate = 5.99;
  const weightRate = weight * 0.5;
  const distanceRate = distance * 0.1;
  const expressMultiplier = isExpress ? 2 : 1;

  return (baseRate + weightRate + distanceRate) * expressMultiplier;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...which then seems to get called in some other part of the codebase like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function processOrder(order) {
  const subTotal = order.items.reduce((sum, item) =&amp;gt; sum + item.price, 0);
  const shipping = calculateShipping(order.weight, order.distance);
  return { total: subTotal + shipping, shipping };
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;About a week later, customers are complaining that express orders are not being charged the express rates.&lt;/p&gt;

&lt;p&gt;The fix is easy, sure, but it's 2026. How come we're still making these errors to begin with?&lt;/p&gt;

&lt;p&gt;*&lt;/p&gt;

&lt;p&gt;In the 1970s, a lot of smart programming-language scientists and academicians &lt;strong&gt;wrote up a bunch of ideas about constructing, describing and writing programs in a way that was different&lt;/strong&gt; to the then-typical &lt;a href="https://en.wikipedia.org/wiki/Von_Neumann_architecture" rel="noopener noreferrer"&gt;&lt;em&gt;von Neumann-style&lt;/em&gt;&lt;/a&gt; (i.e, imperative-style) of programming.&lt;/p&gt;

&lt;p&gt;The idea was not just to be smart, of course. They wanted to write programs in a way that was &lt;strong&gt;easier to &lt;em&gt;reason&lt;/em&gt; about&lt;/strong&gt; , easier for mathematicians to come in and prove the correctness of the programs and, in some weird way, &lt;strong&gt;useful for programmers because it let them not worry about silly errors that were common in the other, &lt;em&gt;imperative&lt;/em&gt;-style of programming.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These ideas have long since escaped the confines of academic esotericism and joined the mainstream through languages like Haskell and OCaml, and also been adopted into many languages including Rust, Swift, Java, Javascript and more.&lt;/p&gt;

&lt;p&gt;What's the point of it all?&lt;/p&gt;

&lt;p&gt;Well, &lt;strong&gt;it's baffling that we continue to produce the same class of errors that we've been producing&lt;/strong&gt; — now, even with AI-generated code — despite the fact that hundreds of smart folks helped found ideas and mechanics of eliminating those entire classes of errors from our programs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Things like &lt;em&gt;undefined&lt;/em&gt; and &lt;em&gt;null-pointer errors&lt;/em&gt;, things like missing a case in a switch/pattern-match (looking at you Typescript), things like totality, things like using generic &lt;em&gt;string&lt;/em&gt; types where a branded-type (custom data-type) would do a much better job of catching errors early&lt;/strong&gt;... you know, just things that make software robust, safe and error-free.&lt;/p&gt;

&lt;p&gt;As humans we made these errors in our codebases. These trained the AI models. Now AI generates similar errors, even if not in as much quantity and frequency. AI is getting better, yes, and there are fewer instances of such errors but the very idea that such errors continue to crop up is supremely silly.&lt;/p&gt;

&lt;p&gt;*&lt;/p&gt;

&lt;p&gt;Let's take the case of &lt;strong&gt;static typing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A lot of languages are dynamically typed and going strong: Python, Javascript, Clojure. But each seemingly has a superset or a library add-on that adds, to varying degrees of robustness, some kind of a type-system. Heck, &lt;strong&gt;an entire superset of Javascript spawned because programmers believe having types and type-guarantees is &lt;em&gt;good&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But even Typescript only goes so far. And the rest, like Clojure Spec or Pydantic, go far less. On the other hand, &lt;strong&gt;strongly type-driven languages like Haskell, Elm and OCaml travel the farthest when it comes to guaranteeing a hell lot of safeties and bug-free programs at conception&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When AI generates dynamically-typed code, you need an elaborate set of unit tests (or property-based tests) to ensure the functions work as expected and handle cases when the input types are wrong. But &lt;strong&gt;when AI generates code in a strong, statically-typed language, a formidable set of static compilation checks (or a language server) ensures that the function implementation makes no mistakes&lt;/strong&gt; with the input and output types.&lt;/p&gt;

&lt;p&gt;Consider a function that adds money:&lt;/p&gt;

&lt;p&gt;It's insufferably easy to get it wrong in Javascript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const addMoney = (a, b) =&amp;gt; a + b;
let a = 24.0; // in EUR
let b = 26.0; // in USD

addMoney(a, b); // makes no sense to add EUR and USD

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typescript introduces a way of safeguarding. It is convoluted, but it works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type EUR = number &amp;amp; { readonly __brand: "EUR" };
type USD = number &amp;amp; { readonly __brand: "USD" };

const eur = (amount: number): EUR =&amp;gt; amount as EUR;
const usd = (amount: number): USD =&amp;gt; amount as USD;

const addEUR = (a: EUR, b: EUR): EUR =&amp;gt; (a + b) as EUR;
const addUSD = (a: USD, b: USD): USD =&amp;gt; (a + b) as USD;

let a = eur(24.0);
let b = usd(26.0);

addEUR(a, a); // ✓ works
addEUR(a, b); // ✗ Type error: USD not assignable to EUR

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's Haskell, a pure-functional programming language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;newtype Money (currency :: Symbol) = Money Double

eur :: Money "EUR"
eur = Money 24.00

usd :: Money "USD"
usd = Money 26.00

addMoney :: Money c -&amp;gt; Money c -&amp;gt; Money c
total = addMoney eur usd -- ERROR: "EUR" ≠ "USD"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler, armed with all the historic logic of strong type-checking, will prevent the code from even compiling in the first place.&lt;/p&gt;

&lt;p&gt;With a decent LSP-LLM integration, which is now very common among the top-AI players, &lt;strong&gt;these kinds of bugs are caught even as the AI tries to produce code&lt;/strong&gt; , and fed back into the AI to correct itself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are many ways of trying to understand programs. People often rely too much on one way, which is called “debugging” and consists of running a partly-understood program to see if it does what you expected. Another way, which ML advocates, is to install some means of understanding in the very programs themselves. - &lt;em&gt;Robin Milner&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Types are not just mechanisms to create safe programs. They are &lt;strong&gt;useful to &lt;em&gt;construct&lt;/em&gt; programs from scratch&lt;/strong&gt;. Types are not just those things you use to set guardrails around how functions are called; they are the basic building blocks. These building blocks &lt;strong&gt;allow us to trace and understand programs far better than slapping a million debug statements&lt;/strong&gt; and running through them tediously.&lt;/p&gt;

&lt;p&gt;All the tooling and mechanics around this is already present. We just need to ask the AI to write programs the way functional programmers write programs: start with types, write the functions around them, and construct entire systems by putting together the pieces... by "composing".&lt;/p&gt;

&lt;p&gt;Nowhere is this made more natural than in languages that support, inherently, the ideas of functional programming. In every other language where these ideas are an afterthought and therefore an add-on package, &lt;strong&gt;it is very easy for AI to escape-hatch itself and default to "just make things work" mode which can result in code that is peppered with errors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;*&lt;/p&gt;

&lt;p&gt;They go by many names. Arrays, lists, vectors, slices, collections. The earliest reference to lists as a formal, mathematical concept appears at around 200 B.C. Computer science, which happens to rely so much on math, implemented arrays in the 1940s and 1950s.&lt;/p&gt;

&lt;p&gt;In 2026, &lt;strong&gt;your app will crash if you have, anywhere in the code, &lt;code&gt;array[1]&lt;/code&gt; and the &lt;code&gt;array&lt;/code&gt; happens to be empty, or worse, a pointer to nothing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The funny thing about this is that &lt;strong&gt;it doesn't matter what industrial-power language you pick: Golang, Python, Javascript (and even Typescript sometimes). Everyone will fail you.&lt;/strong&gt; And the mistake is so prevalent across thousands of codebases that it would be almost impossible for AI not to have trained and reinforced on such ugly, disastrous patterns.&lt;/p&gt;

&lt;p&gt;In FP, it is &lt;em&gt;unidiomatic&lt;/em&gt; to access an index in a list. It is totally do-able, but most programmers prefer the &lt;em&gt;idiomatic&lt;/em&gt; way, which involves a mild tedium called &lt;em&gt;pattern matching&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;at :: [a] -&amp;gt; Int -&amp;gt; Maybe a
at [] _ = Nothing
at (x:_) 0 = Just x
at (_:xs) n = at xs (n - 1)

-- or, better, using Vectors
at' :: Vector a -&amp;gt; Int -&amp;gt; Maybe a
at' v idx = v `!?` idx

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a lot of things happening here, described syntactically, that create near-perfect conditions for the code to not break, crash or go crazy. The &lt;code&gt;at&lt;/code&gt; function tries — that's an important keyword here, &lt;em&gt;tries&lt;/em&gt; — to extract the _n_th element in a list. The three lines are pattern-matches that cover all possible cases: the list is either empty or has at least one element. If your code did not have any one of those lines, the compiler will prevent you from compiling the code. And the result of this extraction is not a guaranteed value of &lt;code&gt;a&lt;/code&gt;. It's &lt;code&gt;Maybe a&lt;/code&gt;, meaning, &lt;strong&gt;if the list was empty to begin with, you extract &lt;code&gt;Nothing&lt;/code&gt;. Downstream, when this function is used on a list, you have to contend with the &lt;code&gt;Nothing&lt;/code&gt;.&lt;/strong&gt; That is, you have to tell the program what to do if the extraction resulted in &lt;code&gt;Nothing&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;getPrimaryEmail :: List String -&amp;gt; String
getPrimaryEmail emails = case at emails 0 of
    Just email -&amp;gt; email
    Nothing -&amp;gt; "noreply@nomail.com"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is this useful? &lt;strong&gt;Why is this important?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thousands of programs have been written this way in functional languages with strong type inference. That means the data that trained the LLMs has this pattern repeat over and over again. And so, &lt;strong&gt;when you ask an LLM to write a Haskell or a Rust program dealing with lists, it emerges with safe code paradigms&lt;/strong&gt; like these making your programs safer, eliminating a class of old, pesky bugs.&lt;/p&gt;

&lt;p&gt;*&lt;/p&gt;

&lt;p&gt;It is quite possible that LLMs eliminate hallucinations entirely. It is also possible (and perhaps happening) where LLM offers a pseudo-determinism in a lot of cache-able (or skill-able) actions, like when we ask it to "write a function to test for prime."&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;the inherent non-determinism of output when asking it do something complex remains. That's not a bug, that's a feature.&lt;/strong&gt;"Write a program to handle a notification queue that is extensible to multiple notification channels," is a wide-ranging ask that will produce a different kind of a code every time you ask the LLM. With each iteration, there may be plenty of sneaky bugs like the ones outlined above (and more).&lt;/p&gt;

&lt;p&gt;"But we have tests!" — yes, tests are quite possibly the finest ways of guaranteeing a program. The formal verification specialist tells me it's not true; formal verification is the surest way. But a majority of the industry doesn't spare any time or resources on formal verification of their programs. &lt;strong&gt;So tests are currently our &lt;em&gt;only&lt;/em&gt; means to ensure an app works as intended&lt;/strong&gt; despite an onslaught of vibe coded slop entering the production lines.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;imagine having to write tests to ensure somewhere in the codebase a function is not called with the wrong parameter type&lt;/strong&gt; or an array's non-existing index is accessed. A whole class of errors are already "catch"-able at compilation; a whole class of human fallacies are already addressed thanks to the work of computational and mathematical geniuses. And not using that seems ridiculous.&lt;/p&gt;

&lt;p&gt;*&lt;/p&gt;

&lt;p&gt;Functional programming is not an easy paradigm to master. The steep learning curve (and the monads) have kept a lot of people away, and in fact driven them (back) into the arms of OOP.&lt;/p&gt;

&lt;p&gt;But with LLMs, the slope of this curve can come down drastically. &lt;strong&gt;Generative AI for code and engineering reduces the need for absolute mastery in a functional language but allows us to reap the benefits of the guarantees and safety&lt;/strong&gt;. It's not yet a perfect balance but it has the best chance of getting there.&lt;/p&gt;

&lt;p&gt;A hell lot of code is going to get generated using LLMs this year. Till the bubble pops when AI companies increase their token prices to realistic values, &lt;strong&gt;organisations are going to build larger throughput pipelines directly from LLM out to production codebase&lt;/strong&gt;. The cost is not just the tokens and the prompt engineers. &lt;strong&gt;The cost is all the slop and the many classes of bugs that are going to get introduced&lt;/strong&gt; which will then need really good engineers (along with AI) to fix. And don't forget the time spent on doing that.&lt;/p&gt;

&lt;p&gt;Part of picking the right stack is picking good, sound languages to build things with. And now might be the best time for software engineers to add FP languages to the stack to reap engineering benefits of academic rigour.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>functional</category>
      <category>haskell</category>
    </item>
    <item>
      <title>Programming rites of passage</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Mon, 02 Feb 2026 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/programming-rites-of-passage-fae</link>
      <guid>https://forem.com/druchan/programming-rites-of-passage-fae</guid>
      <description>&lt;p&gt;Much to my sadistic little delight, I read this morning that vibe-coding (and other similar variations of LLM-driven software building) &lt;a href="https://arxiv.org/pdf/2601.20245" rel="noopener noreferrer"&gt;does not help you acquire skills or learn much&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;LLMs, on the other hand, are trying to give you the sharpest, closest solution possible. When learning through an LLM (especially around bug triangulation, troubleshooting etc), there is hardly any room or potential for ancillary learning.&lt;/p&gt;

&lt;p&gt;Although I have no data on this, I tend to believe that ancillary learning plays a crucial role in our growth as developers and engineers. This is probably true for every other line of work/study.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://notes.druchan.com/claude-vscode-extension-learnings#what-about-ancillary-learning%3F" rel="noopener noreferrer"&gt;from an earlier writeup&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some months back, as I was refining &lt;a href="https://github.com/chandru89new/vaak" rel="noopener noreferrer"&gt;vāk&lt;/a&gt; (my custom blog builder that runs my blog and is used by a grand total of 1 user), I was thinking about the fact that almost every decent programmer I know of has gone through this programmer's rite of passage that involves reinventing, for play and more, many little technologies that already have robust implementations.&lt;/p&gt;

&lt;p&gt;The ordering may be off (I will touch upon that later) but it goes like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first, you build your own (portfolio) site. Instead of relying on a readymade site-builder (squarespace/wordpress back in the day), you would pick something like a framework (ew but nextjs, or astro, or some other obscure stuff) and then build your site. Hundreds of hours of theming, customising, integrating with external data-feeds (like your socials or blog) will ensue.&lt;/li&gt;
&lt;li&gt;then, you level up by porting your journal/blog to a blog-builder of your own. You'll write this in a stack that you're either comfortable with or going gaga about at the time. Hundreds if not thousands of hours will be poured over getting the blog builder just in the right level of abstraction: enough to feel generic (but it's not), but also specific to your use-case in the right way. It's not like anyone else is going to use this but your software-engineering ethics won't let you sleep till you feel it's built like anyone can use it.&lt;/li&gt;
&lt;li&gt;now, armed with the experience of a blog builder, you will head to writing a parser. If you're ambitious, you'll head straight for the obvious — a JSON parser. If not, you might write something that parses far less of a complicated mess. &lt;a href="https://github.com/chandru89new/chequera/blob/main/app/QueryParser.hs" rel="noopener noreferrer"&gt;like extracting content inside SQL-code blocks inside documentation files written in markdown&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;sometimes, it's possible that instead of writing a parser, you might write a library that handles strings of data, either &lt;em&gt;en mass&lt;/em&gt; or in a stream. For the nerds, this might come in the form of implementing a protocol (think redis), or, in the case of lesser-nerds, re-inventing git.&lt;/li&gt;
&lt;li&gt;the final boss is when you write a compiler from scratch for a language. This is meta-level at this point: writing a program to eat, grok and execute other programs. &lt;a href="https://craftinginterpreters.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Crafting Interpreters&lt;/em&gt;&lt;/a&gt; is your new bible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are some more stages here that very few cross. After the compiler, you might get into &lt;a href="https://en.wikipedia.org/wiki/Programming_language_theory" rel="noopener noreferrer"&gt;PL-theory&lt;/a&gt;. Philosophising about "what even is &lt;em&gt;language&lt;/em&gt;? what is &lt;em&gt;grammar&lt;/em&gt;?" will lead you down a path where you might toy with your own language. There is no chance you won't end up doing a bit of LISPy stuff, or get into ML-like syntax before embarking on your own journey, discovering your own weird syntax.&lt;/p&gt;

&lt;p&gt;Or you might branch out into &lt;a href="https://en.wikipedia.org/wiki/Formal_verification" rel="noopener noreferrer"&gt;formal verification&lt;/a&gt; if you're into being a stickler (and into math) for proving things &lt;em&gt;right&lt;/em&gt; (or &lt;em&gt;wrong&lt;/em&gt;, if you derive pleasure out of that). You'll possibly get into reading thesis papers more than layman blogposts (like these) and get into stuff that even Haskell engineers at &lt;a href="https://bellroy.com" rel="noopener noreferrer"&gt;Bellroy&lt;/a&gt; call "esoteric".&lt;/p&gt;

&lt;p&gt;The inquisitive will continue to be so. The perpetually-curious who are intrinsically driven to the details and nuances will continue to be so even in the midst of this vibe-coding AI era.&lt;/p&gt;

&lt;p&gt;I wonder what will happen to those that would &lt;em&gt;like&lt;/em&gt; to skill up but will end up having stunted growth because vibecoding and eager-to-build LLMs are here and the humans are not conscious or aware enough to recognize that the &lt;em&gt;grind&lt;/em&gt; (that the LLM eliminates) is what helps skill-up.&lt;/p&gt;

&lt;p&gt;P.S: The rite of passage is not necessarily sequential. Some folks head straight to &lt;em&gt;Crafting Interpreters&lt;/em&gt;. Some skip the blog builder and have to write parsers at their job. And some formal verification stalwarts have no skill or craving to build their own site from scratch.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>compiler</category>
      <category>parser</category>
      <category>ai</category>
    </item>
    <item>
      <title>LLM, the compiler.</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Fri, 30 Jan 2026 06:29:49 +0000</pubDate>
      <link>https://forem.com/druchan/llm-the-compiler-3ide</link>
      <guid>https://forem.com/druchan/llm-the-compiler-3ide</guid>
      <description>&lt;p&gt;&lt;em&gt;Origin: &lt;a href="https://notes.druchan.com/llm-as-compiler" rel="noopener noreferrer"&gt;https://notes.druchan.com/llm-as-compiler&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"The new lingua franca of programming is English."&lt;/p&gt;

&lt;p&gt;"I don't look at code anymore. I don't look at diffs."&lt;/p&gt;

&lt;p&gt;"There is no way I can look at 3000+ diff patches anymore. It's all spec-driven."&lt;/p&gt;

&lt;p&gt;At first, it's a hard thing to grapple with as a cautious optimist. To not look at code anymore, and yet build things that span hundreds and thousands of lines of code, seems reckless.&lt;/p&gt;

&lt;p&gt;But what is writing JavaScript or C? The code anyway gets compiled and then "translated" into some lower-level language, eventually becoming assembly instructions. Most programmers today do not "look" at the compiled, assembly code, do we? Did the first generation of higher-level language users who almost never looked at the compiled code get frowned upon by those who wrote assembly programs by hand?&lt;/p&gt;

&lt;p&gt;And how is this any different from today? English is the higher-level programming language, the LLM is the compiler.&lt;/p&gt;

&lt;p&gt;The one main (and perhaps the only) difference is the non-determinism that emerges out of the fundamental way in which LLMs work (described as a stochastic parrot) vs. the deterministic outputs of compilers and transpilers. Given a piece of code in a higher language, and a compiler with a set of options, the output in assembly or bytecode will always be the same. This is a reassuring thing because this allows us to write formal "correctness" verification programs for the compilers and guarantee that the outputs will be exactly as expected and not do funny things. Doing this in the "LLM-as-compiler" model seems impossible at this time.&lt;/p&gt;

&lt;p&gt;But, people just don't care. Businesses and organisations are in a perennial race to ship money-making features all the time and software (the way we do it) has this inherent capacity to be built fast (because there is the possibility of iterative improvements and the cost of a bug or failure is 99% of the time not catastrophic). And this "English as the programming language, LLM as the compiler" model helps in this race.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>compilers</category>
      <category>programming</category>
    </item>
    <item>
      <title>6 Aphorisms for AI-assisted Pragmatic Programming</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Fri, 16 Jan 2026 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/6-aphorisms-for-ai-assisted-pragmatic-programming-3797</link>
      <guid>https://forem.com/druchan/6-aphorisms-for-ai-assisted-pragmatic-programming-3797</guid>
      <description>&lt;p&gt;&lt;em&gt;Short aphorisms on AI-assisted (and sometimes AI-driven) software engineering.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. AI-ludditism is futile
&lt;/h3&gt;

&lt;p&gt;AI-skepticism is fair, AI-ludditism is futile.&lt;/p&gt;

&lt;p&gt;AI-assisted programming is here to stay (unless the bubble bursts and leads to prohibitively-expensive tokens).&lt;/p&gt;

&lt;p&gt;"AI has sucked the fun out of programming" is a proxy for AI-ludditism. The entrepreneurial programmer (who loves building things and sees writing code as a means to an end) wins while the puzzle-solver (who loves writing code) will be left behind.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Balance AI-driven and AI-assisted modes of operation to keep your sanity and your job
&lt;/h2&gt;

&lt;p&gt;AI-assisted mode is when you alternate between driving and navigating, lots of checkpoints, have a close-to-intimate understanding of the code that is getting checked-in.&lt;/p&gt;

&lt;p&gt;AI-driven mode is when you let the AI do almost everything, only occasionally pausing to supply a correction, a clarification or a gentle steer.&lt;/p&gt;

&lt;p&gt;AI-assisted mode will help us retain our ability to understand, craft, update and refactor programs without atrophy. It gives us a chance to fight back against slop, and craft robust codebases. AI-driven (of which vibe coding is an extreme end) most likely leads to problems of all kinds.&lt;/p&gt;

&lt;p&gt;Simple rule: Chores? AI-driven. Otherwise = AI-assisted.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Increase your surface area of impact
&lt;/h2&gt;

&lt;p&gt;If AI can work at the level of a mid-engineer, what is our worth to the organization?&lt;/p&gt;

&lt;p&gt;AI enables us engineers to increase the surface area of our impact: a frontend engineer can now build a decent first cut of a feature end-to-end, including backend and database changes; a backend engineer can now extend or add new functionality end-to-end, including frontend and doing some DevOps work.&lt;/p&gt;

&lt;p&gt;You provide value by expanding the surface area of your impact in the team, organization, product.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Bad mental models = Bad instructions = Bad implementation
&lt;/h2&gt;

&lt;p&gt;AI doesn't seem to be able to save you from your bad mental models.&lt;/p&gt;

&lt;p&gt;If your internal mental model of a solution is bad to start with, your instructions to the AI reek of bad assumptions, bad directions and bad examples.&lt;/p&gt;

&lt;p&gt;The basic tenets of software design remain intact. Simple, correct, safe software starts with simple, correct, safe mental models in the human prompter.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Code is still the best documentation, but AI makes grokking easy
&lt;/h2&gt;

&lt;p&gt;Long spec files, implementation.md files and other such gimmicks are trying to be proxies for grokking the code but they all suffer from a plethora of issues (too long to read, outdated and obsolete within days, too many to track etc.).&lt;/p&gt;

&lt;p&gt;Code continues to remain the best source of truth for documentation, because code is what runs and code is what lies underneath a running software.&lt;/p&gt;

&lt;p&gt;Where AI really helps (in teams) is being able to grok the code. AI can summarize, point out the nuances, and even "reason" about many human decisions in the code.&lt;/p&gt;

&lt;p&gt;When working with other people's code, use AI to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Use tests, types and immutability to bring back determinism in a non-deterministic world
&lt;/h2&gt;

&lt;p&gt;Generative AI produces non-deterministic outputs. This is especially scary when you let it modify existing code.&lt;/p&gt;

&lt;p&gt;End-to-end tests, static type checking and functional ideas like immutability and managed side-effects are the antidotes and safeguards against non-deterministic code fiddling.&lt;/p&gt;

&lt;p&gt;E2E tests guarantee the app works as expected for all users. Type checking ensures hallucinated code, hallucinated arguments do not even compile in the first place. And practices like immutability (by using a language that defaults to immutable values) eliminate a class of problems that AI-generated code can bring in (because it has been trained on a lot of human-written code that can be buggy).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Letting the compiler guarantee sortedness</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Wed, 08 Oct 2025 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/letting-the-compiler-guarantee-sortedness-1bbl</link>
      <guid>https://forem.com/druchan/letting-the-compiler-guarantee-sortedness-1bbl</guid>
      <description>&lt;p&gt;It's not often that I wake up on a Sunday and the first thing my brain thinks of is a leetcode problem from two years ago. In fact, it's never. Until today.&lt;/p&gt;

&lt;p&gt;I woke up and found myself mulling over a "gotcha" that I left unaddressed back in June 2023 when solving a fine little leetcode problem.&lt;/p&gt;

&lt;p&gt;The tl;dr of the problem is you're given a list of stock prices for subsequent days and you have to find the maximum profit possible if you bought and sold the stock at most twice. That is, if you had a stock price list of &lt;code&gt;[3, 1, 0, 0, 1, 2, 3]&lt;/code&gt;, the best you can do is buy on the 4th day (for ₹0) and then sell it on 7th day for ₹3, you make a profit of ₹3. (There are some constraints here: you can only buy or sell on a given day; you cannot buy more unless you sell etc. &lt;a href="https://notes.druchan.com/max-stock-profit-leetcode" rel="noopener noreferrer"&gt;Check out the writeup for more info&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SortedArray type is not all that safe&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The solution I managed to cook up worked well but one of the flaws it had was something to do with sorted arrays. In the solution, I needed a &lt;em&gt;sorted&lt;/em&gt; array of elements (elements here being some kind of pairs/tuples). In order to ensure some of the functions only allowed a sorted array (and guaranteed this at the type-level), I created a new type called &lt;code&gt;SortedArray&lt;/code&gt; which, parameterized, looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem was, I could construct a &lt;code&gt;SortedArray&lt;/code&gt; but it could end up being completely unsorted. I just had to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;totallyMisleading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I had to actually make a true-to-its-name &lt;code&gt;SortedArray&lt;/code&gt;, I had to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;betterArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It still doesn't prevent me from doing &lt;code&gt;SortedArray (unsortedArray)&lt;/code&gt; which is a disaster waiting to happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making it impossible to construct a SortedArray unless you use a function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The way to prevent this from happening is to prevent &lt;code&gt;SortedArray&lt;/code&gt; constructor from being usable. That is easy: we put this &lt;code&gt;SortedArray&lt;/code&gt; in a new module and only export the type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SortedArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Prelude&lt;/span&gt;

&lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we add a specific function that generates a &lt;code&gt;SortedArray&lt;/code&gt; from any array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Array&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;toSorted&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;toSorted&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could, at this point, go: what's that &lt;code&gt;Ord a&lt;/code&gt; thingy?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom data types are not really sortable... unless we tell the compiler so&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The key function here is the &lt;code&gt;sort&lt;/code&gt; which is the default "array sort". The expression &lt;code&gt;sort xs&lt;/code&gt; works only because we tell the compiler, through the annotation, that whatever elements we're dealing with in the array are "orderable" (through that &lt;code&gt;Ord a&lt;/code&gt; constraint in the type declaration).&lt;/p&gt;

&lt;p&gt;So this will work out of the box:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;sortedNumbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toSorted&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;-- sortedNumbers == [2,3,4,5,6]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in my original solution, I was dealing with custom data types. Like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;BuySell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BuySell&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An array of elements of these kinds does not lend itself to be sorted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;-- Does not compile.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason is that &lt;code&gt;Ord a&lt;/code&gt; constraint. The compiler does not know how to order an array of &lt;code&gt;StockDay&lt;/code&gt;s or &lt;code&gt;BuySell&lt;/code&gt;s because there is no &lt;code&gt;Ord&lt;/code&gt; implementation for these data types. In English, the compiler just doesn't know how to compare two &lt;code&gt;StockDay&lt;/code&gt;s or two &lt;code&gt;BuySell&lt;/code&gt;s. If it knew that, it would know how to sort an array of &lt;code&gt;StockDay&lt;/code&gt;s or &lt;code&gt;BuySell&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;So, all that was left was to write some &lt;code&gt;Ord&lt;/code&gt; instances for these two custom data types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;ordStockDay&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sortStockDay&lt;/span&gt;

&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;ordBuySell&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="kt"&gt;BuySell&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sortBuySell&lt;/span&gt;

&lt;span class="c1"&gt;-- implementations of these sort functions are cut for brevity&lt;/span&gt;
&lt;span class="c1"&gt;-- sortStockDay :: StockDay -&amp;gt; StockDay -&amp;gt; Ordering&lt;/span&gt;
&lt;span class="c1"&gt;-- sortBuySell :: BuySell -&amp;gt; BuySell -&amp;gt; Ordering&lt;/span&gt;

&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;eqStockDay&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Eq&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt;
&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;eqBuySell&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Eq&lt;/span&gt; &lt;span class="kt"&gt;BuySell&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The &lt;code&gt;Eq&lt;/code&gt; instance derivations are required in order to be able to write &lt;code&gt;Ord&lt;/code&gt; instances for those data types.)&lt;/p&gt;

&lt;p&gt;With these small changes, I could refactor pertinent bits of the code to get a type-level, compile-time guarantee that whatever function needed to use a &lt;code&gt;SortedArray&lt;/code&gt; was getting a sorted array for sure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mucking about in other languages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Typically, these posts conclude with a sentence or two about how great this typeclass-supported polymorphism is and how you could reap the benefits mostly in languages like Haskell/Purescript (or OCaml and maybe Rust).&lt;/p&gt;

&lt;p&gt;But in digging a little deeper into how other languages would implement a type-level guarantee of array sorted-ness even for custom data types and interfaces, I learnt that many languages support these kinds of shenanigans almost out of the box.&lt;/p&gt;

&lt;p&gt;Mostly, it's a matter of declaring the custom "compare" functions for the custom data types and then passing it to the constructors that construct the &lt;code&gt;SortedArray&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;Some allow you to "auto-derive" the sorting if the underlying types already have comparison methods defined in the standard library. For example, a Tuple/Pair. Haskell/Purescript allows us to auto-derive too but auto-derived instances for tuples and custom data types can be tricky. They can be tricky for strings too (should I compare strings lexically or by length?). The key concept here is defining what is a "comparison" is hard for some kinds of data where you have more than one dimension on which you can compare.&lt;/p&gt;

&lt;p&gt;In these implementations, a pattern emerges. We define a custom compare function for the custom data type, then that is passed to the constructor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you passing the comparator implicitly or explicitly?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I say that's passed to the constructor, there are two ways this happens. Either it's explicit: that is, the custom compare function is passed as an argument to the constructor. Or it's implicit: the custom compare function is nowhere in the construction arguments but because a comparison method is available and associated with the datatype, the compiler (or the runtime) knows what to do, how to sort etc.&lt;/p&gt;

&lt;p&gt;In the following examples, assume two things&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we have written a constructor that would construct a &lt;code&gt;SortedArray&lt;/code&gt;. The interface of this constructor will either need explicit passing of the compare function or not.&lt;/li&gt;
&lt;li&gt;the code is not exactly correct. It exists more as an approximation, for illustrating how one gets things done in the langauge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For instance, here's Typescript, an explicit beast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;SD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;BuySell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SD&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Custom comparators&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compareSD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compareBuySell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Constructors; assume "SortedArray" is some class-like implementation that has a "from" method&lt;/span&gt;
&lt;span class="c1"&gt;// from: &amp;lt;T&amp;gt;(T[], compareFn: (a:T,b:T) =&amp;gt; boolean)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sortedSD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SortedArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="nx"&gt;compareSD&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sortedBuySell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SortedArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;buySell1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buySell2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;compareBuySell&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or Golang:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Custom comparison functions&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;compareSD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// some comparison here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;compareBuySell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// some comparison here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Constructors&lt;/span&gt;
&lt;span class="n"&gt;sortedSD&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewSortedArrayWith&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="n"&gt;compareSD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sortedBuySell&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewSortedArrayWith&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bs1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bs2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;compareBuySell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Contrast this with Kotlin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Comparable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;// some custom compare method here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Comparable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;// some custom compare method here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Constructors&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sortedSD&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SortedArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sortedBuySell&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SortedArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sd1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sd2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;BuySell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sd3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sd4&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even the more verbose, Python, we override &lt;code&gt;__lt__&lt;/code&gt;, &lt;code&gt;__eq__&lt;/code&gt; and &lt;code&gt;__gt__&lt;/code&gt; methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@dataclass
class SD:
    first: int
    second: int

    def __lt__ (self, other: 'SD') -&amp;gt; bool:
        # some custom comparison goes here

    def __eq__ (self, other: 'SD') -&amp;gt; bool:
        # some custom equality check goes here

@total_ordering # Generates other comparison methods from __lt__ and __eq__
@dataclass
class BuySell:
    first: SD
    second: SD

    def __lt__ (self, other: 'BuySell') -&amp;gt; bool:
        # Custom: compare by sum of SD values
        self_sum = self.first.first + self.first.second + self.second.first + self.second.second
        other_sum = other.first.first + other.first.second + other.second.first + other.second.second
        return self_sum &amp;lt; other_sum

    def __eq__ (self, other: 'BuySell') -&amp;gt; bool:
        return (self.first, self.second) == (other.first, other.second)

def main() -&amp;gt; None:
    sd_items: List[SD] = [SD(3, 1), SD(1, 2), SD(2, 1)]
    sorted_sd: SortedArray[SD] = SortedArray.from_list(sd_items)

    buysell_items: List[BuySell] = [
        BuySell(SD(1, 2), SD(3, 4)), # sum = 10
        BuySell(SD(2, 1), SD(1, 1)), # sum = 5
        BuySell(SD(5, 5), SD(5, 5)) # sum = 20
    ]
    sorted_buysell: SortedArray[BuySell] = SortedArray.from_list(buysell_items)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beauty of the implicit way is that we can abstract away (from our working memory) how a list/array of custom data type is sorted (or how the elements are compared). And we don't need to pass it on every time we try to generate a sorted list of things.&lt;/p&gt;

&lt;p&gt;Kotlin solves with the &lt;code&gt;Comparable&amp;lt;T&amp;gt;&lt;/code&gt; type which mandates that you write a &lt;code&gt;compareTo&lt;/code&gt; function. Haskell/Purescript solve this with the &lt;code&gt;Ord a&lt;/code&gt; constraint which requires you to provide a &lt;code&gt;compare&lt;/code&gt; function for your custom data types (if you need sorting, comparing functionality for those data types).&lt;/p&gt;

&lt;p&gt;The difference between explicit and implicit is kind of stark if we think about it in layman terms. When we try to construct a &lt;code&gt;SortedArray&lt;/code&gt;, the explicit ones are going, "Okay you asked me to sort a list of things, but can you also tell me how to compare the things?" every time we try to generate a sorted array. The implicit ones are going, "Okay you asked me to sort a list of &lt;em&gt;comparable&lt;/em&gt; things and somewhere in the codebase you've mentioned how to compare this specific thing so I will now construct the sorted array for you."&lt;/p&gt;

&lt;p&gt;There is one pitfall though.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does it even mean to "compare" two things?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is mostly straightforward if we think of numbers. Comparison is greater-than or less-than or equal-to. Booleans can be compared too if we assume True &amp;gt; False.&lt;/p&gt;

&lt;p&gt;But what about strings? Should we compare them just lexically or should we also include the length in the comparison? Or what if, for a specific use-case, we only want to compare the length?&lt;/p&gt;

&lt;p&gt;As an example, how to compare "apple" and "pear" depends on the context. Lexically, "apple" comes first. By length, "pear" comes first. Typically, we would do lexical followed by length — a useful but arbitrary convention.&lt;/p&gt;

&lt;p&gt;Things get complex when we talk about product types. Product types are your objects or structs or dataclasses with multiple fields. These have multiple "dimensions" and what that means is there are many ways of comparing them. And each way could be valid and multiple comparing methods could be necessary.&lt;/p&gt;

&lt;p&gt;Take my &lt;code&gt;StockDay&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="kt"&gt;Price&lt;/span&gt; &lt;span class="kt"&gt;Day&lt;/span&gt;
&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is basically a Tuple, but represented as data for type-level convenience.&lt;/p&gt;

&lt;p&gt;What does comparing or sorting two &lt;code&gt;StockDay&lt;/code&gt;s mean? In my limited use-case, it just means comparing on &lt;code&gt;Price&lt;/code&gt;. But there could be a use-case where I want to sort them by &lt;code&gt;Day&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Ord&lt;/code&gt; instance will completely fail me in this case.&lt;/p&gt;

&lt;p&gt;A naive, incomplete, pair-product-only solution would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;Sortable&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;compareFst&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Ordering&lt;/span&gt;
  &lt;span class="n"&gt;compareSnd&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Ordering&lt;/span&gt;
  &lt;span class="n"&gt;compareBoth&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Ordering&lt;/span&gt;

&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;sortableStockDay&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Sortable&lt;/span&gt; &lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;compareFst&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="kt"&gt;LT&lt;/span&gt; &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="kt"&gt;GT&lt;/span&gt;
  &lt;span class="n"&gt;compareSnd&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;StockDay&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;d1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="kt"&gt;LT&lt;/span&gt; &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="kt"&gt;GT&lt;/span&gt;
  &lt;span class="n"&gt;compareBoth&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;compareFst&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
      &lt;span class="kt"&gt;EQ&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;compareSnd&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;
      &lt;span class="n"&gt;ord&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ord&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there is no way for me to let the compiler or the runtime know dynamically which comparing function to use when I sort an array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;toSorted&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;toSorted&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;-- but notice how I cant tell the sort algo which sort to use&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would have to write a different function for each type of sort:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;toSortedFst&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;Sortable&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;toSortedFst&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sortBy&lt;/span&gt; &lt;span class="n"&gt;compareFst&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;toSortedSnd&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;Sortable&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;toSortedSnd&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sortBy&lt;/span&gt; &lt;span class="n"&gt;compareSnd&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now notice how it's lost some guarantees again because I could end up using the &lt;code&gt;toSortedFst&lt;/code&gt; where I should be using &lt;code&gt;toSortedSnd&lt;/code&gt; and there would be nothing preventing me from doing that. The types would check.&lt;/p&gt;

&lt;p&gt;Languages like OCaml and Rust have context-specific comparisons. So you could essentially have multiple ways to compare and when you try to sort, you can pass it an additional information that tells the compiler/runtime which comparison to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="c"&gt;(* Define the interface *)&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;COMPARABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sig&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
  &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;(* Different comparison strategies *)&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;StringLexical&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;COMPARABLE&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compare&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;StringByLength&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;COMPARABLE&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;StringByLengthThenLexical&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;COMPARABLE&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;len_cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;len_cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;len_cmp&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c"&gt;(* Generic sort function *)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Cmp&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;COMPARABLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="nn"&gt;Cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;

&lt;span class="c"&gt;(* Usage - explicit choice of comparison *)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"hi"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"programming"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="c"&gt;(* Sort lexicographically *)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lexical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;StringLexical&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="nn"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Lexical: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="s2"&gt;"; "&lt;/span&gt; &lt;span class="n"&gt;lexical&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c"&gt;(* Output: Lexical: a; hello; hi; programming; world *)&lt;/span&gt;

  &lt;span class="c"&gt;(* Sort by length *)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;by_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;StringByLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="nn"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"By length: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="s2"&gt;"; "&lt;/span&gt; &lt;span class="n"&gt;by_length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c"&gt;(* Output: By length: a; hi; hello; world; programming *)&lt;/span&gt;

  &lt;span class="c"&gt;(* Sort by length then lexical *)&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;StringByLengthThenLexical&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="nn"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Length then lexical: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="s2"&gt;"; "&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c"&gt;(* Output: Length then lexical: a; hi; hello; world; programming *)&lt;/span&gt;

&lt;span class="c"&gt;(* You can even parameterize functions by comparison strategy *)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;find_max&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Cmp&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;COMPARABLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;hd&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;tl&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fold_left&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nn"&gt;Cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compare&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;hd&lt;/span&gt; &lt;span class="n"&gt;tl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"cat"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"elephant"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s2"&gt;"dog"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;

  &lt;span class="c"&gt;(* Max by lexical order *)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;find_max&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;StringLexical&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Lexical max: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="c"&gt;(* elephant *)&lt;/span&gt;
   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c"&gt;(* Max by length *)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;find_max&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;StringByLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Length max: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="c"&gt;(* elephant *)&lt;/span&gt;
   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're using the same "sort" keyword but we get to instruct the language to use a different algorithm using that &lt;code&gt;(module ....)&lt;/code&gt; declaration. That is rad.&lt;/p&gt;

&lt;p&gt;Arguably, these are edge-cases. It's not often that one needs multiple strategies to compare two things which then affects how one sorts their list. But the fact that language designers in Rust and OCaml thought of these cases as well — and generalised them enough — is quite fascinating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But it's just a wrapper! I shouldn't have to rewrite all methods&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;SortedArray&lt;/code&gt; is merely a wrapper around &lt;code&gt;Array&lt;/code&gt;. But because of the wrapping, any time I want to use an Array-like function (eg &lt;code&gt;head&lt;/code&gt;, or &lt;code&gt;tail&lt;/code&gt; or &lt;code&gt;!!&lt;/code&gt; which is an operator to access the element at an index), I have to re-implement it. Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;

&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- and so on&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'd think that with all the advancements in compilers and type systems and type theory, this overhead wouldn't be required. After all, &lt;code&gt;SortedArray&lt;/code&gt; is just a wrapper around a simple &lt;code&gt;Array&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Of course, languages like Haskell/Purescript lets you &lt;em&gt;kind of&lt;/em&gt; get rid of this overhead.&lt;/p&gt;

&lt;p&gt;To do this, we just do some "derive"s:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;newtypeSortedArray&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Newtype&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt;
&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;functorSortedArray&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Functor&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt;
&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;foldableSortedArray&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Foldable&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt;
&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;traversableSortedArray&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Traversable&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt;
&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;applySortedArray&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Apply&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt;
&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;applicativeSortedArray&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Applicative&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt;
&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;bindSortedArray&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Bind&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt;
&lt;span class="n"&gt;derive&lt;/span&gt; &lt;span class="kr"&gt;newtype&lt;/span&gt; &lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;monadSortedArray&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Monad&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the ability to run operations like &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;fold&lt;/code&gt; but for other array operations like &lt;code&gt;length&lt;/code&gt;, we have to write our functions. Instead of doing unwrap/wrap over and over again, the &lt;code&gt;newtype&lt;/code&gt; derivation allows us to simply use a generic function called &lt;code&gt;coerce&lt;/code&gt; which does the unwrapping and wrapping for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.NewType&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coerce&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All this is great but there are downsides and flaws to this approach.&lt;/p&gt;

&lt;p&gt;When we derive newtype instance for a type like &lt;code&gt;SortedArray&lt;/code&gt;, the compiler makes the &lt;code&gt;SortedArray&lt;/code&gt; constructor "exposed". That goes all the way back to my original problem with my old solution — exposing the constructor means I could construct a non-sorted-array and wrap it in &lt;code&gt;SortedArray&lt;/code&gt; to make it look like it is sorted. But it won't be.&lt;/p&gt;

&lt;p&gt;The other issue, specific to this use-case, is that deriving functions like &lt;code&gt;map&lt;/code&gt; automatically does not keep the sorted-ness guarantee.&lt;/p&gt;

&lt;p&gt;This is wrong (and depending on the usage, dangerous):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coerce&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;

&lt;span class="n"&gt;wrong&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;-- wrong == SortedArray [-1,-2,-3,-4]&lt;/span&gt;

&lt;span class="n"&gt;alsoWrong&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;-- alsoWrong == SortedArray [1,2,3,-6,-7]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there's nothing that informs me of that unless I peak into the mapping function.&lt;/p&gt;

&lt;p&gt;The correct &lt;code&gt;map&lt;/code&gt; goes like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SortedArray&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toSorted&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromSorted&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;-- or more succinctly&lt;/span&gt;
&lt;span class="c1"&gt;-- map f = toSorted &amp;lt;&amp;lt;&amp;lt; Array.map f &amp;lt;&amp;lt;&amp;lt; fromSorted&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a handful of other functions that would break sorting if &lt;code&gt;coerce&lt;/code&gt;-d through newtype derivations. Like &lt;code&gt;mapWithIndex / indexedMap&lt;/code&gt;, &lt;code&gt;insert&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;cons/snoc/push&lt;/code&gt; etc. Anything that updates elements (in-place or as a new result) while retaining the array-like structure is a problem.&lt;/p&gt;

&lt;p&gt;I started this writeup as a journal entry after toying around with &lt;code&gt;SortedArray&lt;/code&gt;'s compiler guarantees. But as I wrote, I began exploring around and ended up in various rabbit holes of parameterisation, polymorphism and typeclass-like behaviours of other languages.&lt;/p&gt;

&lt;p&gt;Some wonderful abstractions here if you are mindful of the gotchas.&lt;/p&gt;

</description>
      <category>purescript</category>
      <category>functional</category>
      <category>haskell</category>
      <category>typeclass</category>
    </item>
    <item>
      <title>AI Productivity Kool-Aid</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Fri, 08 Aug 2025 06:30:00 +0000</pubDate>
      <link>https://forem.com/druchan/ai-productivity-kool-aid-469d</link>
      <guid>https://forem.com/druchan/ai-productivity-kool-aid-469d</guid>
      <description>&lt;p&gt;A polarised world is usually a disaster but the entertainment-quotient is enormous. And so, as much as I dislike the kind of polarisation that's happening in my small little world of software developers about AI and LLM-driven software engineering, I tell you, it is incessantly entertaining.&lt;/p&gt;

&lt;p&gt;On the one hand, the clickbait headlines and the trying-too-hard-not-to-sound-like-a-promotion-for-my-business opening salvos on LinkedIn posts have made all pronouncements there are to be made about the game-changing nature of AI-driven engineering and product-building. On the other, small little indie blogs are silently declaring war on the same thing, citing dead-end goose-chases and wasted productivity from the very same AIs.&lt;/p&gt;

&lt;p&gt;What is actually happening? This is left to academics who have enough time to study. The engineers — both the ones vibing their way into a feature or out of a bug, and the rest who just canceled their Cursor subscription and went back to neovim — simply don't have the &lt;em&gt;bandwidth&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The verdict from the academics (to the simple-sounding question "Does AI make the software dev more productive?") seems laced with the most common phrase you hear in software circles: &lt;em&gt;it depends&lt;/em&gt;. (Well, they reported a &lt;a href="https://arxiv.org/abs/2507.09089" rel="noopener noreferrer"&gt;19% decrease&lt;/a&gt; in a narrow group).&lt;/p&gt;

&lt;p&gt;The landscape is quite divided and there are no clear answers yet. Vibe coders are producing software artefacts in the time it would've taken a senior developer to setup the frontend. But elsewhere, the state-of-the-art model has already misdirected someone in a 2-hour hair-pulling bug hunt. The joke is that the bug was written by Claude in the first place during that vibe coding session.&lt;/p&gt;

&lt;p&gt;A single vibe-coded artefact brings surprise and shock at the same time — founders and top-level collars are surprised at the result because they go, "imagine the possibilities now!", and software engineers are shocked at the codebase because, "who the f— writes code like this?" (plus, "oh god, now &lt;em&gt;I&lt;/em&gt; have to clean this mess up within unreasonable timelines).&lt;/p&gt;

&lt;p&gt;Amidst the haze, I think some clarity has been achieved collectively. We now understand that this AI-driven productivity gains is quite possible in some scenarios; notably, building prototypes from scratch and grunt-work that's easy to verify. Elsewhere, disaster rules.&lt;/p&gt;

&lt;p&gt;Founders and top-level managers who seldom code but have hundreds of ideas they'd like to build and profit from have the ultimate tool — building prototypes of what they have been dreaming about is easier than ever before and involves very little money, very little time and, often, very little coaxing of the LLMs.&lt;/p&gt;

&lt;p&gt;Grunt-work that you would otherwise debate between writing an automation for or just doing manually — and end up spending way too much time trying to write that automation script, &lt;a href="https://xkcd.com/1319/" rel="noopener noreferrer"&gt;immortalised by Randall&lt;/a&gt; — is now a couple of prompts away (usually).&lt;/p&gt;

&lt;p&gt;Between these poles of birthing a prototype through vibe-coding and letting an LLM loose on mindless grunt-work, there is a large swath of land. A land of complex, large codebases needing a feature add. Of imperfect code with duplications and edge-case bugs and occasional reflection of human fallacies requiring large-scale refactors. Of codebases in obscure languages or frameworks asking for bug fixes. The folks at the poles have tasted immense success and productivity through AI and what they expect is that the folks dealing with this large, unruly land tame the wilderness with AI. It is a reasonable expectation in a perfect world where patterns are unblemished and consistent. Alas, LLMs are anything but.&lt;/p&gt;

&lt;p&gt;But beyond all this, there's something that bothers me.&lt;/p&gt;

&lt;p&gt;What to do with the productivity gains? Are you going to empower people to spend less time on work and more time on their personal lives or are you going to make them march to death to the tunes of profit-maximisation and shareholder happiness?&lt;/p&gt;

&lt;p&gt;If we go by the historic behaviour of the industrialised world, there's only sad news. Technological progress has brought more work-hours and less work-life balance — exactly the opposite of things technological progress was supposed to offer — to the lives of the workers. This new kid on the tech-progress block is no different.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>llm</category>
    </item>
    <item>
      <title>Embracing ReaderT finally</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Fri, 18 Apr 2025 19:21:07 +0000</pubDate>
      <link>https://forem.com/druchan/embracing-readert-finally-4j56</link>
      <guid>https://forem.com/druchan/embracing-readert-finally-4j56</guid>
      <description>&lt;p&gt;&lt;em&gt;Intended audience: programmers, esp those with functional-programming background.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My last outing with the &lt;code&gt;ReaderT&lt;/code&gt; monad was a &lt;a href="https://notes.druchan.com/haskell-journal-day-5" rel="noopener noreferrer"&gt;minor disaster&lt;/a&gt; and I ended up writing a custom wrapper that got the job done. At the time, the monad-stacking behavior was new to me and I couldn't make sense of the code I was getting into.&lt;/p&gt;

&lt;p&gt;Recently, I added a global-config in my blog-generator project (&lt;a href="https://github.com/chandru89new/vaak" rel="noopener noreferrer"&gt;&lt;code&gt;vāk&lt;/code&gt;&lt;/a&gt;). A handful of things needed to be dynamic and set from the shell env, and these get set into the global config which then gets passed around the app in multiple places.&lt;/p&gt;

&lt;p&gt;I thought it was time for another outing with the &lt;code&gt;ReaderT&lt;/code&gt; monad.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vāk&lt;/code&gt;'s codebase is simple, in the sense that almost all effectful functions are just &lt;code&gt;Aff a&lt;/code&gt;, which get rolled into top-level &lt;code&gt;ExceptT&lt;/code&gt; functions and the &lt;code&gt;main&lt;/code&gt; function "runs" these top-level functions. At a high-level, this just meant that I had to retain the same behavior but only change the top-level function to be &lt;code&gt;ReaderT&lt;/code&gt; wrapping around an &lt;code&gt;ExceptT&lt;/code&gt; and the main function just runs &lt;code&gt;runReaderT&lt;/code&gt; and &lt;code&gt;runExceptT&lt;/code&gt; (which I can compose into a &lt;code&gt;runAppM&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;But of course, a lot of &lt;code&gt;Aff a&lt;/code&gt; functions were internally calling a &lt;code&gt;getConfig&lt;/code&gt; function (which is the global config getter). So, the idea was make those functions which called &lt;code&gt;getConfig&lt;/code&gt; use the &lt;code&gt;ReaderT&lt;/code&gt; monad transformer's &lt;code&gt;ask&lt;/code&gt; function and that means those functions became &lt;code&gt;ReaderT&lt;/code&gt; instead of &lt;code&gt;Aff&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And thus began the conversion of all such functions.&lt;/p&gt;

&lt;p&gt;Some functions still remained &lt;code&gt;Aff&lt;/code&gt; because either global config was passed to them as a parameter, or they did not use the global config at all.&lt;/p&gt;

&lt;p&gt;Composing an &lt;code&gt;Aff&lt;/code&gt; and a &lt;code&gt;ReaderT&lt;/code&gt; meant that I had to "lift" the &lt;code&gt;Aff&lt;/code&gt; into &lt;code&gt;ReaderT&lt;/code&gt;. The &lt;code&gt;liftAff&lt;/code&gt; instance does not work though because it does not "handle" errors. So, I ended up rolling my own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;AppM&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="n"&gt;aff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ReaderT&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;aff&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the &lt;code&gt;Aff&lt;/code&gt; is run, errors are "caught" via &lt;code&gt;try&lt;/code&gt;, converted into an &lt;code&gt;Either&lt;/code&gt; and then all of that is rolled into an &lt;code&gt;ExceptT&lt;/code&gt;, wrapped around a &lt;code&gt;ReaderT&lt;/code&gt; leading to my &lt;code&gt;AppM&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;AppM&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ReaderT&lt;/span&gt; &lt;span class="kt"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I liked how I was able to, with this &lt;code&gt;liftAppM&lt;/code&gt;, gradually move functions around until everything was neatly converted.&lt;/p&gt;

&lt;p&gt;For instance, if this was the pre-ReaderT function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Config&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;initializeStuff&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;processBlogPosts&lt;/span&gt;
    &lt;span class="c1"&gt;-- and about 15 more lines of function calls, each in the 'Aff' monad&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;AppM&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt;
    &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;initializeStuff&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
      &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;processBlogPosts&lt;/span&gt;
      &lt;span class="c1"&gt;-- rest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I'll see that &lt;code&gt;processBlogPosts&lt;/code&gt; calls for &lt;code&gt;config&lt;/code&gt; internally, so I'd have to make it use &lt;code&gt;ask&lt;/code&gt; and therefore, turn it into an &lt;code&gt;AppM&lt;/code&gt;. This means I'd also move function above it (&lt;code&gt;initializeStuff&lt;/code&gt;) into an &lt;code&gt;AppM&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;AppM&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt;
    &lt;span class="n"&gt;initializeStuff&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;processBlogPosts&lt;/span&gt;
    &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
        &lt;span class="c1"&gt;-- rest of the stuff&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, I could gradually move things out of the inner &lt;code&gt;liftAppM&lt;/code&gt; and finally get rid of it altogether. Just the logging bits remained in a generic &lt;code&gt;m&lt;/code&gt; monad and could be lifted into my &lt;code&gt;AppM&lt;/code&gt; via &lt;code&gt;liftAppM&lt;/code&gt; so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;AppM&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="s"&gt;"Starting..."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;createFolderIfNotPresent&lt;/span&gt; &lt;span class="n"&gt;tmpFolder&lt;/span&gt;
  &lt;span class="n"&gt;postsMetadata&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;generatePostsHTML&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Posts page generated."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Generating archive page..."&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;writeArchiveByYearPage&lt;/span&gt; &lt;span class="n"&gt;postsMetadata&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Archive page generated."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Generating home page..."&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;createHomePage&lt;/span&gt; &lt;span class="n"&gt;postsMetadata&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Home page generated."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Copying 404.html..."&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;liftEffect&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cp "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;templateFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/404.html "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tmpFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;defaultExecSyncOptions&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"404.html copied."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="s"&gt;"Copying images folder..."&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;liftEffect&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cp -r "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"./images "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tmpFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;defaultExecSyncOptions&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"images folder copied."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="s"&gt;"Copying js folder..."&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;liftEffect&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cp -r "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"./js "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tmpFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;defaultExecSyncOptions&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="s"&gt;"js folder copied."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="s"&gt;"Generating styles.css..."&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;generateStyles&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="s"&gt;"styles.css generated."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="s"&gt;"Generating RSS feed..."&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;Rss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;generateRSSFeed&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;postsMetadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="s"&gt;"RSS feed generated."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Copying "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tmpFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;" to "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outputFolder&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;createFolderIfNotPresent&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outputFolder&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;liftEffect&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cp -r "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tmpFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/* "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outputFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;defaultExecSyncOptions&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="s"&gt;"Copied."&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="s"&gt;"Updating cache..."&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeCacheData&lt;/span&gt;
  &lt;span class="n"&gt;liftAppM&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="s"&gt;"Cached updated."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are (monad-reader-transformer) &lt;code&gt;lift&lt;/code&gt; instances for both &lt;code&gt;Aff&lt;/code&gt; and &lt;code&gt;ExceptT&lt;/code&gt; but both will fail to handle errors.&lt;/p&gt;

&lt;p&gt;Here's the implementation for &lt;code&gt;ExceptT&lt;/code&gt; that will get called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="n"&gt;monadTransExceptT&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;MonadTrans&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;lift&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the &lt;code&gt;m&lt;/code&gt; action throws an error, the app just crashes. Typically, I'd like for it to be returned as a &lt;code&gt;Left&lt;/code&gt; value (the way non-errors are getting returned as &lt;code&gt;Right&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;I am still not very clear about the mechanics here though; tests with native monad-reader-transformer instances of &lt;code&gt;lift&lt;/code&gt; for both &lt;code&gt;Aff&lt;/code&gt; and &lt;code&gt;ExceptT&lt;/code&gt; seem to crash the app instead of allowing the user to handle them in a custom way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Effect&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;launchAff_&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;getConfig&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;runAppM&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;lift&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;testAff&lt;/span&gt;
  &lt;span class="n"&gt;liftEffect&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;logShow&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;

&lt;span class="n"&gt;testAff&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Either&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;testAff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;throwError&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="s"&gt;"test error"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;test&lt;/code&gt; will crash the app.&lt;/p&gt;

&lt;p&gt;Hence, the custom (and poorly-named) &lt;code&gt;liftAppM&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>functional</category>
      <category>purescript</category>
      <category>haskell</category>
      <category>monad</category>
    </item>
    <item>
      <title>A Minimal Static Blog Generator in Purescript - My Notes and Code Walkthrough</title>
      <dc:creator>druchan</dc:creator>
      <pubDate>Sun, 13 Apr 2025 12:54:01 +0000</pubDate>
      <link>https://forem.com/druchan/a-minimal-static-blog-generator-in-purescript-my-notes-and-code-walkthrough-f4j</link>
      <guid>https://forem.com/druchan/a-minimal-static-blog-generator-in-purescript-my-notes-and-code-walkthrough-f4j</guid>
      <description>&lt;p&gt;&lt;em&gt;Intended audience: basic functional programming know-how, Haskell/Purescript newbies/enthusiasts.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;About two years ago, I decided to move my Tumblr blog to some sort of a custom-made static blog generating tool written in Purescript (a Haskell-inspired language that compiles to JS).&lt;/p&gt;

&lt;p&gt;I've wanted to chronicle the work on this codebase at various times but never got around to doing it. Somehow, I convinced myself that the write-up would be boring to readers and not of &lt;del&gt;much&lt;/del&gt; any value.&lt;/p&gt;

&lt;p&gt;Nevertheless, I decided that I'll at least jot down some notes from the codebase and maybe someone finds it interesting enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/chandru89new/vaak" rel="noopener noreferrer"&gt;GitHub source of the project&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why another static blog/site generator? And why Purescript?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Coming out of Tumblr, I vaguely remember looking at some static site options. Many of them were feature behemoths (Next.js). Smaller, fancy ones like 11ty caught my eye but it still felt ceremonious to get started with them.&lt;/p&gt;

&lt;p&gt;On the other hand, I was itching to do something in Purescript (I've learnt stuff better by building things).&lt;/p&gt;

&lt;p&gt;So it was just a matter of wanting to learn a language and also to end up creating a nice little, ultra-minimal static blog generating setup for myself.&lt;/p&gt;

&lt;p&gt;Too often, people end up in the "do not reinvent the wheel" mentality. This is great mantra for a business/startup. But if you want to learn things, you have to constantly reinvent stuff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's in a static blog generator?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the very core of static site/blog generators, it is all about generating HTML pages. You run a script and it collects all the data it needs and then writes HTML pages to your disk. You then host these HTML pages as your site/blog.&lt;/p&gt;

&lt;p&gt;When I started this project, this was my basic, high-level mental model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I will write posts in markdown files that are saved inside a &lt;code&gt;contents&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;When I run the script, it will read the contents of each markdown file, convert that into HTML, then use a "template" where it will replace the body with the HTML and save the final result as an HTML file.&lt;/li&gt;
&lt;li&gt;I will also generate, through the script, an &lt;code&gt;index.html&lt;/code&gt; that will render all posts in reverse chronological order. You know, like a blog.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The build steps are basically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get a list of markdown files from a folder (these are the blog posts to generate HTML files for)&lt;/li&gt;
&lt;li&gt;for each file: read the contents, convert markdown into HTML, replace the body from a template string with this HTML, write the final thing to an HTML file.&lt;/li&gt;
&lt;li&gt;finally, generate an index HTML file (from a template), replacing the body with a list of all posts, linking each post to its HTML file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually, I ended up adding more bits like an RSS feed, an archive page, a more customized &lt;code&gt;index&lt;/code&gt; page etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building from the smallest unit of work&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can either solve things top-down (write the main function first, then fill out the constituent functions) or bottom-up (write functions for the smallest unit of work and then compose upwards).&lt;/p&gt;

&lt;p&gt;In this one, I went bottom-up for the most part. &lt;/p&gt;

&lt;p&gt;The smallest unit of work was something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read a markdown file&lt;/li&gt;
&lt;li&gt;convert the contents into HTML&lt;/li&gt;
&lt;li&gt;replace a template string with that HTML&lt;/li&gt;
&lt;li&gt;write the updated template string to a file with the same slug/name as the markdown file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trickiest part was converting markdown to HTML. There is a library in Purescript that does this but I can't remember why I did not use it and instead, used something called an &lt;a href="https://book.purescript.org/chapter10.html" rel="noopener noreferrer"&gt;FFI&lt;/a&gt; to tap into JS code.&lt;/p&gt;

&lt;p&gt;I have a JS module that exports a helper function (which takes a markdown string, and converts that into an HTML string, and returns an object of that data).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Utils.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;md2FormattedDataService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MarkdownIt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;md2RawFormattedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;matter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;frontMatter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YYYY-MM-DD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;md2FormattedDataService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is then imported into my Purescript module (the filename is same as that of the JS file. &lt;code&gt;Utils&lt;/code&gt;) as a foreign-function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Utils.purs&lt;/span&gt;
&lt;span class="n"&gt;foreign&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;md2RawFormattedData&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;RawFormattedMarkdownData&lt;/span&gt;

&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;RawFormattedMarkdownData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I can compose a function that does the unit of work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;writeHTMLFile&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Template&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;FormattedMarkdownData&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;writeHTMLFile&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;askConfig&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;writeTextFile&lt;/span&gt; &lt;span class="kt"&gt;UTF8&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmpFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replaceContentInTemplate&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
    &lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logError&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Could not write "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".md to html ("&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;
    &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Wrote: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contentFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".md -&amp;gt; "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tmpFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".html"&lt;/span&gt;
  &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;

&lt;span class="n"&gt;replaceContentInTemplate&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Template&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;FormattedMarkdownData&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;replaceContentInTemplate&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Template&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;replaceAll&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Pattern&lt;/span&gt; &lt;span class="s"&gt;"{{title}}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Replacement&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;a href=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;./"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;/a&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;
    &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;replaceAll&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Pattern&lt;/span&gt; &lt;span class="s"&gt;"{{content}}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Replacement&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;replaceAll&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Pattern&lt;/span&gt; &lt;span class="s"&gt;"{{date}}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Replacement&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;formatDate&lt;/span&gt; &lt;span class="s"&gt;"MMM DD, YYYY"&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;replaceAll&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Pattern&lt;/span&gt; &lt;span class="s"&gt;"{{page_title}}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Replacement&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;replaceContentInTemplate&lt;/code&gt; function is a pure, side-effect-less function which replaces marked slots (like &lt;code&gt;{{content}}&lt;/code&gt;) in a string template with the right data, producing a final HTML string.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;writeHTMLFile&lt;/code&gt; function, I just try and write the file with the contents thus produced. &lt;/p&gt;

&lt;p&gt;What's worth noting is that the function is designed never to fail. It always returns &lt;code&gt;unit&lt;/code&gt; (via &lt;code&gt;pure unit&lt;/code&gt;) because if things go wrong, they should not crash the building. (That is to say, if one post does not get converted into an HTML file for whatever reason, it should not prevent others from getting converted).&lt;/p&gt;

&lt;p&gt;To do this, I use a &lt;code&gt;try&lt;/code&gt; which takes a function that &lt;em&gt;could&lt;/em&gt; throw, and converts it into an &lt;code&gt;Either&lt;/code&gt; value — that is, if the function throws, you will get a &lt;code&gt;Left&lt;/code&gt; value. If not, a &lt;code&gt;Right&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;A part that's missing in the code snippet is the &lt;code&gt;md2rawFormattedData&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;readFileToData&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="kt"&gt;FormattedMarkdownData&lt;/span&gt;
&lt;span class="n"&gt;readFileToData&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;readTextFile&lt;/span&gt; &lt;span class="kt"&gt;UTF8&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;
  &lt;span class="kr"&gt;let&lt;/span&gt;
    &lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;md2FormattedData&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;

    &lt;span class="n"&gt;fdraw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;md2RawFormattedData&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;
  &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;InvalidStatus&lt;/span&gt; &lt;span class="kr"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;throwError&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Invalid status in "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Found -&amp;gt; status: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fdraw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;
  &lt;span class="kr"&gt;else&lt;/span&gt;
    &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As it turns out, the script (at the time of writing this) reads all markdown files and converts them in a different step and then passes that list of converted data through the script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel processing in Purescript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are a bunch of options in Purescript to do parallel computation. I faced this need quite early. &lt;/p&gt;

&lt;p&gt;The whole deal is about processing a bunch of files. That is, running them through the same functions.&lt;/p&gt;

&lt;p&gt;So once I had a set of functions that could process one file, I could simply &lt;code&gt;traverse&lt;/code&gt; through them. &lt;code&gt;Traverse&lt;/code&gt; is functional-programming-speak for looping through data (typically, list-like/array) and performing some action every time on the data while also accumulating the results. Think of JS's &lt;code&gt;map&lt;/code&gt; but imagine each item is not just transformed but you do some side-effectful action as well.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;filePathsToProcessedData&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Config&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;FormattedMarkdownData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;filePathsToProcessedData&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;fpaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parTraverse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;readFileToData&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contentFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;fpaths&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I am trying to work through an array of file paths (&lt;code&gt;Array String&lt;/code&gt;). Each file path is passed to the &lt;code&gt;readFileToData&lt;/code&gt; (which converts a markdown file to its HTML equivalent, see above). And I can do this in parallel trivially by using the &lt;code&gt;parTraverse&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;parTraverse&lt;/code&gt; the result of each computation is retained and returned in the same data structure that was passed. So, passing an array would result in returning an array of values corresponding to each application/computation of &lt;code&gt;readFileToData&lt;/code&gt;. That's why you see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;readFileToData&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="kt"&gt;FormattedMarkdownData&lt;/span&gt;
&lt;span class="n"&gt;filePathsToProcessedData&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Config&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;FormattedMarkdownData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is, the &lt;code&gt;filePathsToProcessedData&lt;/code&gt; function, which uses &lt;code&gt;parTraverse&lt;/code&gt; returns an array (inside an &lt;code&gt;Aff&lt;/code&gt; context, which denotes some side-effectful action) of &lt;code&gt;FormattedMarkdownData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is also a variant of &lt;code&gt;parTraverse&lt;/code&gt; where you don't care about the results. It's called &lt;code&gt;parTraverse_&lt;/code&gt;. I use that when generating HTML files from the set of converted posts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;generatePostsHTML&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;FormattedMarkdownData&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;generatePostsHTML&lt;/span&gt; &lt;span class="n"&gt;fds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;readPostTemplate&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;parTraverse_&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;writeHTMLFile&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Template&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;fds&lt;/span&gt;
  &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Side-effects, Aff monad, errors and the ExceptT transformer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Side effects in languages like Haskell and Purescript are usually of the type &lt;code&gt;IO&lt;/code&gt; or &lt;code&gt;Eff&lt;/code&gt;/&lt;code&gt;Aff&lt;/code&gt; (Aff is asynchronous, whereas &lt;code&gt;Eff&lt;/code&gt; is synchronous).&lt;/p&gt;

&lt;p&gt;It took me a while to get used to these. To be clear, it's easy to write a function that uses one of these. What is harder is to then "compose" them in other functions and also to handle errors correctly. With a better understanding of the monadic composition and experience with some transformers (i.e one level higher abstraction over monads), things became a little easier.&lt;/p&gt;

&lt;p&gt;The blog generator is peppered with &lt;code&gt;Aff&lt;/code&gt; everywhere because there's reading from files and writing to files going on and all of these are side-effects from a functional programming standpoint.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Aff&lt;/code&gt;s (and &lt;code&gt;IO&lt;/code&gt;s and &lt;code&gt;Eff&lt;/code&gt;s) can throw errors. Unfortunately, this is not explicit in the type information. You just assume that these computations can fail and throw an error, causing your app/script to crash.&lt;/p&gt;

&lt;p&gt;One way to handle this is to wrap the computation in a &lt;code&gt;try&lt;/code&gt; function; this will change a result from &lt;code&gt;Aff a&lt;/code&gt; (where &lt;code&gt;a&lt;/code&gt; is any type, called polymorphic type) to &lt;code&gt;Aff (Either e a)&lt;/code&gt; (where &lt;code&gt;e&lt;/code&gt; = any error type). By converting it to &lt;code&gt;Either&lt;/code&gt;, you capture the error as a value. &lt;/p&gt;

&lt;p&gt;The downside to this is that composition becomes difficult.&lt;/p&gt;

&lt;p&gt;That is, before using &lt;code&gt;try&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;doAction1&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;doAction2&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="c1"&gt;-- can be composed easily:&lt;/span&gt;
&lt;span class="n"&gt;doCombined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;doAction1&lt;/span&gt;
  &lt;span class="n"&gt;doAction2&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But with &lt;code&gt;try&lt;/code&gt;, in an attempt to catch errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;doAction1&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Either&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;doAction2&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Either&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;doCombined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;doAction1&lt;/span&gt;
  &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
    &lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doAction2&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is, I have to pattern match and extract. This gets harder and complex when more functions are involved in the computation/combination.&lt;/p&gt;

&lt;p&gt;To circumvent this issue, we can use something called monad transformers. Specifically for our case, &lt;code&gt;ExceptT&lt;/code&gt;. (all monad transformers are idiomatically typed with an ending capital &lt;code&gt;T&lt;/code&gt; — &lt;code&gt;ExceptT&lt;/code&gt;, &lt;code&gt;EitherT&lt;/code&gt;, &lt;code&gt;ReaderT&lt;/code&gt;, etc.)&lt;/p&gt;

&lt;p&gt;I can now rewrite this as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;doAction1&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;doAction2&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="n"&gt;doCombined&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;doCombined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;doAction1&lt;/span&gt;
    &lt;span class="n"&gt;doAction2&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;

&lt;span class="c1"&gt;-- and then to actually run the computation:&lt;/span&gt;
&lt;span class="n"&gt;doCombinedRun&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Either&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;doCombinedRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runExceptT&lt;/span&gt; &lt;span class="n"&gt;doCombined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ExceptT&lt;/code&gt; allows me to retain the simple, straight-forward coding style without using &lt;code&gt;try&lt;/code&gt; and pattern matching the &lt;code&gt;Either&lt;/code&gt; value, but it bakes error-handling in for free.&lt;/p&gt;

&lt;p&gt;Things of type &lt;code&gt;ExceptT&lt;/code&gt; are really actions &lt;em&gt;to be done&lt;/em&gt;. That is, they have to be run. To do this, we have &lt;code&gt;runExceptT&lt;/code&gt; which is of this type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;runExceptT&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Either&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- or more concretely in my case&lt;/span&gt;
&lt;span class="n"&gt;runExceptT&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Either&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes an ExceptT and "runs" it, and if it threw an error, you get an &lt;code&gt;Aff (Left e)&lt;/code&gt; and if it ran the computation successfully, it returns an &lt;code&gt;Aff (Right a)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Armed with this information, and excited that now I can simplify the app, I converted a lot of functions to be &lt;code&gt;ExceptT&lt;/code&gt;s. But this posed a new problem: I had to unwrap actions (ie, run them) a few times at different places. That re-introduced the pattern matching code.&lt;/p&gt;

&lt;p&gt;Eventually, I figured a way to not do that and instead, just have a top-level ExceptT instead.&lt;/p&gt;

&lt;p&gt;So, the app's core functions — eg, building the site (&lt;code&gt;buildSite&lt;/code&gt;) — are of the &lt;code&gt;ExceptT&lt;/code&gt; type but all the functions that go into composing the &lt;code&gt;buildSite&lt;/code&gt; and other core functions are just &lt;code&gt;Aff&lt;/code&gt;s. They can throw errors, sure, but they'll all be caught in the top-level, core functions.&lt;/p&gt;

&lt;p&gt;As an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Config&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ExceptT&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;-- a lot of `Aff a` functions go here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the &lt;code&gt;main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;launchAff_&lt;/span&gt;
    &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;askConfig&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;runExceptT&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;buildSite&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
        &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logError&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Error when building the site: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="s"&gt;"Site built and available in the "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outputFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;" folder."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where a function throwing an error should not be bubbled up, I do an in-place &lt;code&gt;try&lt;/code&gt; and catch the errors early:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;writeHTMLFile&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Template&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;FormattedMarkdownData&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="kt"&gt;Unit&lt;/span&gt;
&lt;span class="n"&gt;writeHTMLFile&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;askConfig&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;writeTextFile&lt;/span&gt; &lt;span class="kt"&gt;UTF8&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tmpFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;replaceContentInTemplate&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
    &lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logError&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Could not write "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".md to html ("&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt;
    &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kt"&gt;Logs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Wrote: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contentFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".md -&amp;gt; "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tmpFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;frontMatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".html"&lt;/span&gt;
  &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Passing global config around&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In recent times, I've been trying to make my blog generator as much configurable as possible. The templates folder, the contents folder etc. that had been historically hard-coded are now customizable through environment variables (shell env).&lt;/p&gt;

&lt;p&gt;This meant that all the functions that used these hard-coded values would now need to &lt;em&gt;depend&lt;/em&gt; on a global config. So, I'd either have to pass the config as another function parameter/argument, or make these functions &lt;em&gt;read&lt;/em&gt; from the global config somehow.&lt;/p&gt;

&lt;p&gt;There are two approaches possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;initialize the global config at the start of the app (typically, the &lt;code&gt;main&lt;/code&gt; function) and then pass the config object down the wire to all functions. This would lead to what React devs call &lt;em&gt;prop-drilling&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;or, use the &lt;code&gt;ReaderT&lt;/code&gt; monad transformer pattern where functions can "ask" for the config object and then use it, but the moment functions do this, they become side-effectful functions (if they already weren't).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I decided that for my script, I did not have to go all the way to a &lt;code&gt;ReaderT&lt;/code&gt;, and I could just get away with a simple &lt;code&gt;askConfig&lt;/code&gt; function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;askConfig&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="kt"&gt;Config&lt;/span&gt;
&lt;span class="n"&gt;askConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;liftEffect&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;templateFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;lookupEnv&lt;/span&gt; &lt;span class="s"&gt;"TEMPLATE_DIR"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fromMaybe&lt;/span&gt; &lt;span class="n"&gt;defaultTemplateFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;outputFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;lookupEnv&lt;/span&gt; &lt;span class="s"&gt;"OUTPUT_DIR"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fromMaybe&lt;/span&gt; &lt;span class="n"&gt;defaultOutputFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;contentFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;lookupEnv&lt;/span&gt; &lt;span class="s"&gt;"POSTS_DIR"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fromMaybe&lt;/span&gt; &lt;span class="n"&gt;defaultContentFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;totalRecentPosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;lookupEnv&lt;/span&gt; &lt;span class="s"&gt;"RECENT_POSTS"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;templateFolder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;templateFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outputFolder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;outputFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contentFolder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contentFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newPostTemplate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;defaultBlogpostTemplate&lt;/span&gt; &lt;span class="n"&gt;templateFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;totalRecentPosts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;totalRecentPosts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
  &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fromMaybe&lt;/span&gt; &lt;span class="n"&gt;defaultTotalRecentPosts&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For functions that already are &lt;code&gt;Aff a&lt;/code&gt;, I can get the config without disturbing the function's type. For those that are "pure", I just pass the config as the first argument.&lt;/p&gt;

&lt;p&gt;This refactor was simple but I also remember feeling happy for the guarantees of the Purescript compiler in making sure I was refactoring things without breaking them in the process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding a simple cache mechanism to optimize build times&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A full (cache-less) build of the site with ~140 posts takes ~3 seconds so there is really no need for a caching mechanism but it's one of those niceties to have, so I added that in.&lt;/p&gt;

&lt;p&gt;Figuring out a simple cache logic was interesting. The goal was to know what files got modified and what didn't and using system commands (&lt;code&gt;stat&lt;/code&gt;) made it quite easy.&lt;/p&gt;

&lt;p&gt;Each file can then be mapped to its modification datetime and stored as some kind of key-value thing in a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a-breeze-of-wisdom::Feb 28 14:18:11 2025 Feb 28 14:18:11 2025
a-naive-thought::Mar  1 11:54:00 2025 Mar  1 11:54:00 2025
a-vengeful-society-vs-a-corrective-society::Feb 28 14:18:11 2025 Feb 28 14:18:11 2025
aaji::Feb 28 14:18:11 2025 Feb 28 14:18:11 2025
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the time of building the site, I just read from this file, check the last modified datetime value of the file and compare it with current, and decide if the file needs a re-build.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;getStat&lt;/code&gt; function helps get the stat data which is then used for the cache generation. A &lt;code&gt;parTraverse&lt;/code&gt; helps compose &lt;code&gt;getStat&lt;/code&gt; over a list/array of files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;getStat&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;FilePath&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;getStat&lt;/span&gt; &lt;span class="n"&gt;contentsFolder&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;liftEffect&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"stat -f &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%Sm %Sc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; -n "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;contentsFolder&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".md"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;defaultExecSyncOptions&lt;/span&gt;
  &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
    &lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;try&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;liftEffect&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;toString&lt;/span&gt; &lt;span class="kt"&gt;UTF8&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;
      &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Right&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kt"&gt;Left&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;getStatAll&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Aff&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stat&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;getStatAll&lt;/span&gt; &lt;span class="n"&gt;slugs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;askConfig&lt;/span&gt;
  &lt;span class="n"&gt;parTraverse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getStat&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contentFolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;slugs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;More commands!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a long time, invoking the blog generator script would immediately start building the blog. But CLI tools usually have dedicated commands that print help, version etc.&lt;/p&gt;

&lt;p&gt;So I modified the code to do some of those things.&lt;/p&gt;

&lt;p&gt;Going for an arg-parsing library for this (in Haskell, something like &lt;code&gt;optparse-applicative&lt;/code&gt;, in Purescript, something like the &lt;code&gt;yargs&lt;/code&gt; library?) seemed overkill so I went with a simple pattern match that took the args, and then converted it into a &lt;code&gt;Command&lt;/code&gt; type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Command&lt;/span&gt;
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Build&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ShowVersion&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Help&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;NewPost&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Invalid&lt;/span&gt;

&lt;span class="n"&gt;mkCommand&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Array&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Command&lt;/span&gt;
&lt;span class="n"&gt;mkCommand&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
  &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="s"&gt;"version"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ShowVersion&lt;/span&gt;
  &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="s"&gt;"help"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Help&lt;/span&gt;
  &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="s"&gt;"build"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Build&lt;/span&gt;
  &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="s"&gt;"new"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
    &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;NewPost&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;
    &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Invalid&lt;/span&gt;
  &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Invalid&lt;/span&gt;

&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;
  &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;mkCommand&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;
  &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
    &lt;span class="kt"&gt;Help&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;helpText&lt;/span&gt;
    &lt;span class="c1"&gt;-- ...rest of the code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "NewPost" was a recent addition: it allowed me to create a new blogpost using a template so that I didn't have to type out all the metadata.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The complexities of using TailwindCSS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The project uses/supports use of Tailwind CSS classes and directives so I could pepper my templates and stylesheets and even markdown with Tailwind's CSS classes.&lt;/p&gt;

&lt;p&gt;The tricky bit comes where I have to use Tailwind generate the final, compiled CSS file.&lt;/p&gt;

&lt;p&gt;I generate the site in a dedicated temporary folder — so all rendered HTML files are put there, along with copied JS/CSS/image files in respective folders. &lt;em&gt;Then&lt;/em&gt;, I run Tailwind to compile the final CSS output. And once all that is done, I just copy over everything into a &lt;code&gt;public&lt;/code&gt; folder which can then be used to host/serve.&lt;/p&gt;

&lt;p&gt;Here, I have two options: use Tailwind CLI to compile it, or use Tailwind via NPM to do the same.&lt;/p&gt;

&lt;p&gt;Using Tailwind via NPM means installing &lt;code&gt;tailwindcss&lt;/code&gt; and then invoking &lt;code&gt;npx @tailwind/cli&lt;/code&gt;. This creates a &lt;code&gt;node_modules&lt;/code&gt; folder, a &lt;code&gt;package.json&lt;/code&gt; and a &lt;code&gt;package-lock.json&lt;/code&gt; which I have to then cleanup. An older version of the generator did precisely all this.&lt;/p&gt;

&lt;p&gt;In the new version, the script assumes that you have the Tailwind CLI standalone installed on your machine, and is invokable as a simple &lt;code&gt;tailwindcss&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Eventually, I'd like to improve logging and customization around this. For example, if you do not rely on Tailwind classes and roll your own, you should be able to set a flag or something to disable my script from running the Tailwind command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple, colorful logging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://github.com/chandru89new/chequera" rel="noopener noreferrer"&gt;another project for testing Steampipe queries&lt;/a&gt;, I use a simple pattern for logging with colors for success, error and warning/info. I decided to use that in this project too.&lt;/p&gt;

&lt;p&gt;The logging looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzio7e12rg16gz6tes2x3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzio7e12rg16gz6tes2x3.png" alt="logging image" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is just a bunch of simple functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[31m"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m"&lt;/span&gt;

&lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[32m"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m"&lt;/span&gt;

&lt;span class="n"&gt;yellow&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;yellow&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[33m"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m"&lt;/span&gt;

&lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[34m"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m"&lt;/span&gt;

&lt;span class="n"&gt;logSuccess&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="s"&gt;"[SUCCESS]: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;logInfo&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="s"&gt;"[INFO]: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;logError&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="s"&gt;"[ERROR]: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;logWarning&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yellow&lt;/span&gt; &lt;span class="s"&gt;"[WARNING]: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Where to from here?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is one of those little things that you're proud of and have fun tinkering around with. There are no goals other than just having fun while I improve the functionality and also keep it simple and useful enough.&lt;/p&gt;

&lt;p&gt;Of the many side-projects I've worked on the years, this is one of those that I use regularly. That makes this project somewhat special.&lt;/p&gt;

</description>
      <category>functional</category>
      <category>haskell</category>
      <category>purescript</category>
      <category>blog</category>
    </item>
  </channel>
</rss>
