<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>code.druchan.com — RSS Feed</title>
    <link>https://code.druchan.com/</link>
    <description>Journal
    of a littany of things from Druchan</description>
    <lastBuildDate>Thu, 12 Dec 2024 12:00:00 +0530</lastBuildDate>
    <atom:link
      href="https://code.druchan.com/feed.xml" rel="self" type="application/rss+xml" />
    <item>
<title>Advent of Code and Aesthetics</title>
<link>https://code.druchan.com/aoc-aesthetics-haskell</link>
<guid>https://code.druchan.com/aoc-aesthetics-haskell</guid>
<description><![CDATA[<p>Over the last few days, I've been solving <a href="https://adventofcode.com/2024/">Advent of Code 2024</a> puzzles in Haskell. This is only my second time hurting my brain by trying to come up with solutions for AoC (<a href="https://github.com/chandru89new/elm-aoc">last time was in 2021</a>, when I used Elm; I barely made it to Day #15 before giving up).</p>
<p>AoC puzzles expose me to many sorts of list and graph-type data structure puzzles, with some accompanying search (usually DFS) / update algorithms. As someone who comes from a non-CompSci background with very weak math acumen (one of the reasons I did not pursue a masters in Physics), all of this hurts but it's nevertheless so much fun to think of an algorithm, express it in Haskell syntax (or try to) and then run the code to see it output what has so far ended up being the right answers to the AoC puzzles.</p>
<p>Some notes arose out of this.</p>
<h3>&quot;Thinking in types&quot; does not go brrrr</h3>
<p>It's all fun and games to think in types. In fact, it makes sense when I am <a href="https://github.com/chandru89new/rdigest">building something</a>. But boy does it suck to introduce sum types and such when doing these puzzles. I started the first couple of days with a type-based approach but by <a href="https://github.com/chandru89new/aoc2024/blob/main/app/Day6.hs">Day 6</a>, I turned away from it. It's useful when I can afford to spend a whole lot of time building types and functions for data transformation but not when I just want to solve a damn puzzle that just involves a whole bunch of <code>Char</code>s.</p>
<h3>My algorithms lack mathematical aesthetics</h3>
<p>One of the wonderful after-effects of me discovering the <a href="https://en.wikipedia.org/wiki/Functional_programming">world of FP</a> is that I could model something as data-transformations. A lot of things can be modelled like that and the triumvirate of immutability, applicative/monadic laws, and strong typing make it a delightful experience. AoC puzzles literally are about data transformations. Great.</p>
<p>But when I compare my solutions to those of someone like <a href="https://github.com/abhin4v/AoC24">Abhinav</a>, good lord, my programs feel so imperative. Almost every puzzle lends itself to some really aesthetic mathematical jugglery, sometimes simple, sometimes complex. I guess having a good mathematical bent helps in discovering or inventing these aesthetic-looking solutions (yes, aesthetics is subjective, I know). Haskell is well-suited to expressing these mathematical things almost verbatim. Too bad I have not the level of acumen or knowledge.</p>
<h3>REPLing is fun till that large computation hits you</h3>
<p><a href="https://blog.cleancoder.com/uncle-bob/2020/05/27/ReplDrivenDesign.html">REPL-based development</a> is a gift. I am sad that it's not the norm in many environments (like frontend, except <a href="https://www.youtube.com/watch?v=toGEegAzrZA">when it's Clojurescript</a>). While not as exceptional as Clojure, Haskell's REPL is great and it accelerates development.</p>
<p>Except, during some of these AoC puzzles, the computation does take a while and it's a long pensive wait because sometimes I can't tell if the code went into an infinite recursion or it's just taking a long time. (Long times here mean ~120s, which doesn't feel all that long on paper but when other puzzles solve in under a second or two, 120 is huuuuuuge).</p>
<p>I compiled the program to test some other mechanism I was building into my code unrelated to some long-running puzzles, and then I ran the binary, running the puzzle inadvertently. Suddenly, the ~120s function ran in a fraction of that time! Crap. I could've saved a lot of anxiety by just building the binary (which takes &lt;5s) and running the solutions!</p>
<h3>Resisting the urge to be (point-)free</h3>
<p>Several times in a coding session, there's this opportunity to <a href="https://wiki.haskell.org/Eta_conversion">reduce an expression</a>, often to the extent of <a href="https://wiki.haskell.org/index.php?title=Pointfree">point-free</a>. It is tempting. But I imagine myself reading this code a few weeks down the line and I can vividly picture a completely confused brain that struggles to comprehend what it wrote. That keeps the terseness-shenanigans at bay. However, it would be cool to have a branch where the code is as terse as can be.</p>
<p>*</p>
<p>Anyway, it is almost always a delight to be able to solve some puzzles by writing some code. Amidst some stressful work caretaking for a recovering parent from their surgery, I look forward to these moments when I get to think about these AoC puzzles.</p>
]]></description>
<pubDate>Thu, 12 Dec 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 16</title>
<link>https://code.druchan.com/haskell-journal-day-16</link>
<guid>https://code.druchan.com/haskell-journal-day-16</guid>
<description><![CDATA[<p>One of the things that has been bugging me since I wrapped most bits on the <code>rdigest</code> project was that I could not get it to work in a Github repo where I planned to run it on a cron so that digests get created automatically every day and hosted somewhere so I can read from anywhere.</p>
<p>My first attempts to do this was to upload my locally-built binary and see if that works in a GH action running on a <code>macos-latest</code> machine — it did not. I spent a tiny bit of time before my day-job took precedence and because I couldn't get it to work, I decided to park there and come back later.</p>
<p>Coming back later, I decided to make the <code>rdigest</code> repo build its binaries on <code>ubuntu-latest</code> and release the artefact in <a href="https://github.com/chandru89new/rdigest/releases">the repo</a>. This was exceedingly simple (probably needed a couple of iterations to get the right configuration options for GHCup, Cabal, GHC, tagging etc.). This worked nice, and all that was left to do was to consume the release in my <a href="https://github.com/chandru89new/rdigest-data"><code>rdigest-data</code> repo's</a> cron action.</p>
<p>With a few iterations, I got all of it tied up. The cron ran once every day and updated the digest for a particular day. With GH Pages setup on that repo, I was able to just hit the URL and see a list of all digests and read each digest at leisure.</p>
<p>But this unearthed a new problem: since I ran the digest update just once, and the process would only update for &quot;today&quot; (whatever today was at the time of running the binary), some posts could &quot;slip&quot; the digest depending on the timing. I haven't spent much time thinking about the optimal approach to fixing this but in the meantime, it made sense to just update all digest-days once I have refreshed (and saved posts from) all feeds in my list. Ugly, nuclear solution but it ensures all my digest files are up-to-date.</p>
<p>Life is getting a lot in the way in recent times so there's been a pause in activity but I am itching to refine the <code>rdigest</code> codebase to elegantly separate out the functional and imperative bits. And also revisit what interesting things the project can spawn into and make it more useful than just producing digests.</p>
]]></description>
<pubDate>Wed, 27 Nov 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 15</title>
<link>https://code.druchan.com/haskell-journal-day-15</link>
<guid>https://code.druchan.com/haskell-journal-day-15</guid>
<description><![CDATA[<ul>
<li>
<p>One of the next things I decided to do was to set up the entire <code>rdigest</code> workflow somewhere it could run on a schedule and produce digest files as output. I didn’t think much about this initially; I simply discussed with ChatGPT the shortest and easiest way to host the digest files (just plain HTML with some in-built styling). My thought process was to work backward: first, get the digest HTML files hosted and served from somewhere (I decided on GitHub Pages as the easiest solution), and then figure out how to update/produce daily digests using a cron job. I assumed GitHub Actions would suffice for the task.</p>
</li>
<li>
<p>The hosting bit worked fine, except I didn’t have an <code>index.html</code> file, so GitHub Pages threw a 404 error until I created and uploaded one to the repository. The Action/Workflow file was also generated within seconds, as ChatGPT seemed to have anticipated my next move. However, what didn’t work was that the binary failed to run—a cryptic exit code <code>137</code> abruptly terminated the runner, and the entire action quit. I spent some time tinkering with troubleshooting options, but my efforts were half-hearted. By this time, other complications had surfaced. For example: &quot;How would I update the feeds list if I wanted to add a new feed?&quot;</p>
</li>
<li>
<p>I decided to pause my efforts on this and form better ideas around how to transfer the entire experience to &quot;the cloud&quot; so that <code>rdigest</code> would be available to me wherever I go, even if I don’t have access to my personal machine.</p>
</li>
<li>
<p>In some aimless meandering through threads on the Haskell channel on the Functional Programming Slack, I stumbled upon a link to <a href="https://www.youtube.com/watch?v=w9ExsWcoXPs&amp;ab_channel=OST%E2%80%93OstschweizerFachhochschule">Gabriella Gonzalez's talk on Monad Transformers</a>. <code>rdigest</code> had gone through a phase where I experimented with monad transformers (<code>ReaderT</code> and <code>ExceptT</code>), but I had to abandon that work due to my poor understanding of transformers at the time. The talk rekindled my interest in them, and I’ve been considering revisiting the idea of transforming the app to use <code>ReaderT</code> at the very least. Gonzalez mentioned avoiding <code>ExceptT</code> in favor of lazy <code>IO</code>, and I find myself inclined to agree with that perspective.</p>
</li>
<li>
<p>I also happened to read some reiterations on the beauty of domain modeling through types and the functional-core/imperative-shell concept. I couldn’t help but recognize that my code is an unabridged mishmash of functional and imperative directives. A lot of it feels imperative to me, and I think I might benefit—sooner rather than later—from taking a closer look at the structure of the code. Refining (or even refactoring) the app to clearly delineate between <code>IO a</code> functions and pure ones seems worthwhile. The app currently has a heavy effectful stance because it involves substantial reading and writing to the database and the file system, along with <code>fetch</code>-like calls. At the same time, it also includes a host of purely functional operations. Clearly separating these would likely improve the maintainability and clarity of the codebase.</p>
</li>
</ul>
]]></description>
<pubDate>Mon, 04 Nov 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 14</title>
<link>https://code.druchan.com/haskell-journal-day-14</link>
<guid>https://code.druchan.com/haskell-journal-day-14</guid>
<description><![CDATA[<ul>
<li>I added 286 YouTube channel RSS feeds (from channels I’m subscribed to) to my personal <code>rdigest</code> collection. This surfaced some usability challenges. For any given day, more than 70% of the links were from YouTube, highlighting the need for better categorization.</li>
<li>Initially, feeds were grouped by the source URL, but this wasn't sufficient for YouTube because all feed URLs have the same hostname (<code>youtube.com</code>). I needed a way to distinguish them by the feed title. However, I wasn’t capturing the title, and <code>title</code> wasn’t even a column in the <code>feeds</code> table. So, I updated the database schema to add a <code>title</code> column—essentially setting up a basic migration system. I experimented with a simple &quot;up-only&quot; migration approach, which worked reasonably well and now allows me to run multiple SQL queries from a file in sequence within a transaction.</li>
<li>Adding titles to feeds enabled grouping by URL while displaying titles, making the digest easier to scan. This required reconsidering how I structured the data. I went from grouping as <code>(URL, [FeedItem])</code> (where <code>FeedItem</code> represents an individual post from the feed at <code>URL</code>) to <code>((URL, String), [FeedItem])</code>. Though straightforward at the type level, the change needed adjustments in functions. The digest grouping now happens within the function that writes the digest file since that's where grouping is required. It’s a relief to be able to use tuples as keys without having to worry — imagine facing this in the Javascript world.</li>
<li>Once again, I enjoyed the process of writing code as if functions already existed, defining type annotations and <code>undefined</code> placeholders, and then iteratively filling in the <code>undefined</code> parts with help from the language server and occasionally ChatGPT.</li>
<li>Another crazy thing happened with the binary size. The un-stripped one (but with all optimizations from cabal and GHC) is over 80MB. Stripping it of symbols reduces it to 51MB. I was randomly searching for discussions about Haskell binary sizes and discovered a tool called <code>upx</code>. Using that, the binary size dropped to 15MB. Can you imagine?!</li>
</ul>
]]></description>
<pubDate>Fri, 25 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 13</title>
<link>https://code.druchan.com/haskell-journal-day-13</link>
<guid>https://code.druchan.com/haskell-journal-day-13</guid>
<description><![CDATA[<ul>
<li>
<p>Added an important update: rdigest requires you to specify where to store the db and digest files via environment variable. This was surprisingly easy to implement.</p>
</li>
<li>
<p>Attempted to add subreddit RSS, which proved very temperamental. It works sometimes, but often no feed comes through because Reddit aggressively blocks direct access by tools/scrapers. Went on a troubleshooting journey trying to solve this by using alternative libraries like wreq, but these didn't help. All these &quot;simpler&quot; libraries were actually much harder to get started with and use. While this arguably makes them more typesafe, they are poor for rapid prototyping. The examples in their documentation are also inadequate. Returned to the original library (Network.HTTP.Simple) and added a user-agent header, which occasionally fixes the Reddit RSS issue.</p>
</li>
<li>
<p>There are spots in the code where better use of available instances for particular datatypes could improve readability. For example, in the <code>runApp</code> where the environment variable is retrieved:</p>
</li>
</ul>
<pre><code class="language-haskell">runApp :: App a -&gt; IO ()
runApp app = do
  let template = $(embedFile &quot;./template.html&quot;)
  rdigestPath &lt;- lookupEnv &quot;RDIGEST_FOLDER&quot;
  case rdigestPath of
    Nothing -&gt; showAppError $ GeneralError &quot;It looks like you have not set the RDIGEST_FOLDER env. `export RDIGEST_FOLDER=&lt;full-path-where-rdigest-should-save-data&gt;&quot;
    Just rdPath -&gt; do
      pool &lt;- newPool (defaultPoolConfig (open (getDBFile rdPath)) close 60.0 10)
      let config = Config {connPool = pool, template = BS.unpack template, rdigestPath = rdPath}
      res &lt;- (try :: IO a -&gt; IO (Either AppError a)) $ app config
      destroyAllResources pool
      either showAppError (const $ return ()) res
</code></pre>
<ul>
<li>
<p>In this code, <code>app</code> returns an <code>IO a</code> which is handled as a potentially-throwing action. However, the extraction of <code>rdPath</code> happens outside the <code>try</code>. Better code would follow the happy-path pattern, with errors handled inherently in a global <code>try</code> block. The <code>runApp</code> function is intended to be that global try block for all <code>App a</code> actions, but the environment variable extraction happens outside this boundary.</p>
</li>
<li>
<p>At some point in this Haskell journey, the project began feeling like routine code similar to Typescript. This prompted reflection on the benefits derived from using Haskell beyond learning the language and code architecture. Notable advantages include:</p>
<ul>
<li>Easier writing and reading of async/effectful actions due to <code>bind</code> abstractions (sugared as <code>do</code> blocks) with upper-level error handling</li>
<li>Clean implementation of applicative parsing, which would be challenging in non-functional programming languages</li>
<li>Convenient data types and constructors that simplify logic construction</li>
</ul>
</li>
<li>
<p>Hlint often recommended ways to make code more concise through <a href="https://wiki.haskell.org/Pointfree">point-free</a> style or using operators. For example, this code:</p>
</li>
</ul>
<pre><code class="language-haskell">parseURL :: String -&gt; Maybe URL
parseURL url = case parseURI url of
  Just uri -&gt; (if uriScheme uri `elem` [&quot;http:&quot;, &quot;https:&quot;] then Just url else Nothing)
  Nothing -&gt; Nothing
</code></pre>
<p>Can be written as:</p>
<pre><code class="language-haskell">parseURL :: String -&gt; Maybe URL
parseURL url = parseURI url &gt;&gt;= \uri -&gt; if uriScheme uri `elem` [&quot;http:&quot;, &quot;https:&quot;] then Just url else Nothing
</code></pre>
<ul>
<li>
<p>Sometimes explicit destructuring and imperative-style expressions were preferred for better understanding of the logic.</p>
</li>
<li>
<p>This was largely a matter of familiarity. With more exposure to situations requiring unwrapping double monadic structures, the use and understanding of <code>&gt;&gt;=</code> became more natural:</p>
</li>
</ul>
<pre><code class="language-haskell">getDomain :: Maybe String -&gt; String
getDomain url =
  let maybeURI = url &gt;&gt;= parseURI &gt;&gt;= uriAuthority
   in maybe &quot;&quot; uriRegName maybeURI
</code></pre>
<ul>
<li>
<p>This experience recalled the <a href="https://mail.haskell.org/pipermail/haskell-cafe/2009-March/058475.html">exchange</a> on terseness and readability.</p>
</li>
<li>
<p>I think sometimes I am able to think very cleanly thanks to the type-system and the functional-style of programming but there's certainly the feeling that I am not completely tapping into that potential. Being able to express the logic in the code as a beautiful equation is still elusive.</p>
</li>
</ul>
]]></description>
<pubDate>Mon, 21 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 12</title>
<link>https://code.druchan.com/haskell-journal-day-12</link>
<guid>https://code.druchan.com/haskell-journal-day-12</guid>
<description><![CDATA[<ul>
<li>
<p>All improvements made on <a href="./haskell-journal-day-11">day 11</a> went to waste because the app kept crashing when I tried to 'refresh' the feeds. It either crashed with a <code>bus error</code> message, a <code>malloc</code> related error, or in the worst case scenario, just got stuck while the machine ran out of memory and my MacBook asked me to force quit some apps.</p>
</li>
<li>
<p>I went on a wild goose chase trying to isolate the issue and then test various combinations. I isolated the issue to the fetching of feeds from the feed URLs, but all sorts of changes to that function did not help. Going lazy ByteString didn't help. Using an alternative library (wreq, req etc.) didn't help either.</p>
</li>
<li>
<p>I asked a bunch of folks via the usual help channels: FP slack #haskell channel, FP India Telegram channel, and reddit.</p>
</li>
<li>
<p>I spent a day obsessing about this, trying everything. I then decided to give it a break because it was getting on my nerves.</p>
</li>
<li>
<p>Finally, the breakthrough came when I asked on IRC (Libra server, #haskell). One of them pointed to a known issue with <code>ghc &lt; v9.2.6</code> in the <code>GMP</code> module (which I, being ignorant, obviously had no clue about). Updating my <code>cabal</code> and <code>ghc</code> just magically fixed the issue. My app finally is able to terminate correctly and function absolutely well! (I don't mind the 1G memory footprint it has when it refreshes all feeds).</p>
</li>
<li>
<p>One learning did come out of this though: I was concatenating strings and forming SQLite query strings that way — but I was advised to use parameterization, so that change was incorporated.</p>
</li>
<li>
<p>After almost two days of drudgery and not having any idea about how to fix the crashes, there's now excitement that the app works and I can continue adding features to it.</p>
</li>
</ul>
]]></description>
<pubDate>Fri, 18 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 11</title>
<link>https://code.druchan.com/haskell-journal-day-11</link>
<guid>https://code.druchan.com/haskell-journal-day-11</guid>
<description><![CDATA[<ul>
<li>
<p>Made significant improvements to the tool. Now able to refresh a single feed and build a digest for a date range. The digest selects items with <code>published</code> dates (<code>updated</code> in the database) within the specified range. Added a command to create a digest for today.</p>
</li>
<li>
<p>Updated code to handle edge cases, such as attempting to process a feed not yet added to the database. This prevents issues related to missing feed IDs in the database affecting the feed_items table.</p>
</li>
<li>
<p>Implemented numerous improvements to <a href="https://i.imgur.com/4GJi0bd.png">the template</a>. Error messages are now more user-friendly and informative.</p>
</li>
<li>
<p>Discovered multiple feed items with <code>null</code> updated values due to the datetime parser returning <code>Nothing</code>. Added four new date formats to address this issue:</p>
</li>
</ul>
<pre><code class="language-haskell">parseDate datetime = fmap utctDay $ firstJust $ map tryParse [fmt1, fmt2, fmt3, fmt4, fmt5, fmt6]
   where
     fmt1 = &quot;%Y-%m-%dT%H:%M:%S%z&quot;
     fmt2 = &quot;%a, %d %b %Y %H:%M:%S %z&quot;
     fmt3 = &quot;%a, %d %b %Y %H:%M:%S %Z&quot;
     fmt4 = &quot;%Y-%m-%dT%H:%M:%S%Z&quot;
     fmt5 = &quot;%Y-%m-%dT%H:%M:%S%Q%z&quot;
     fmt6 = &quot;%Y-%m-%dT%H:%M:%S%Q%Z&quot;
     ...rest of the code
</code></pre>
<ul>
<li>
<p>Renamed the project from <code>rss-digest</code> to <code>rdigest</code>.</p>
</li>
<li>
<p>Progress on Haskell-specific learning has slowed. Excitement is waning due to lack of challenges outside the comfort zone. Considering adding server capabilities to the tool, allowing it to serve the digest. This would involve UI updates to accept date ranges and implementing server functionality.</p>
</li>
</ul>
]]></description>
<pubDate>Thu, 17 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 10</title>
<link>https://code.druchan.com/haskell-journal-day-10</link>
<guid>https://code.druchan.com/haskell-journal-day-10</guid>
<description><![CDATA[<ul>
<li>
<p>The digest list I generated seemed bland and unfriendly as a single list, so I grouped them based on the feed URL (eventually, feed title). This improved the experience of viewing a day's digest, as it now appears as a <a href="https://imgur.com/sk14hRb">grouped list</a>. This change also required updates to the template.html file.</p>
</li>
<li>
<p>For grouping, I initially considered using the Map datatype but felt it was overkill. Instead, I decided to write small functions to handle a datatype which is Map-like <code>[(key, value)]</code>.</p>
</li>
<li>
<p>I occasionally encounter &quot;bus errors,&quot; which are usually related to out-of-memory issues. I suspect this is due to Haskell's laziness. These errors typically occur when fetching feeds, but randomly. I need to investigate the cause.</p>
</li>
<li>
<p>I'm now considering having this tool spin up a local server to serve the digest on-demand. Commands could also be invoked from the web UI. Alternatively, I could explore concurrency to allow parts of the feed item refresh process to occur simultaneously.</p>
</li>
</ul>
]]></description>
<pubDate>Wed, 16 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 9</title>
<link>https://code.druchan.com/haskell-journal-day-9</link>
<guid>https://code.druchan.com/haskell-journal-day-9</guid>
<description><![CDATA[<ul>
<li>Finally managed to add some variant of a &quot;daily digest&quot; system today.</li>
<li>Took a while to think of a bare minimum implementation of the digest logic. The table captures <em>when</em> a feed item/post was added (basically, when you run a <code>refresh</code>, it gets all the posts from all the RSS feeds you've added and adds the posts—those that aren't already in the DB—along with the current date, which is different from the published/updated date). This gives me a way to prepare a daily digest for any given day: I just have to pick the items for that day.</li>
<li>The pros in this logic are that I can prepare a digest for any day, and it's kind of simple (maybe too simple). It's also idempotent unless you do a refresh on the same day and there are new feeds added. The cons, of course, are many: there's no way to mark an item as read, so what you have is a growing list of posts as you read them—but I'm okay with this implementation for now. As I work through and use the system, I'll find more ideas to refine or pivot.</li>
<li>I think I was at that familiar stage of any side-project where I was doing a lot of side-quests instead of writing the main piece: the &quot;daily digest&quot; logic. Added other niceties: confirmation steps for removals, a purge/nuke option, and more critically, modified the schema so that feed items/posts are foreign-key linked to the feed in the feeds table. So, if I delete a feed, all the posts associated with it are also removed by SQLite, thanks to the constraint.</li>
<li>Haskell lint offered a lot of interesting suggestions to make the code more concise. I found that in a few places, I had to suppress or ignore the suggestions to keep the code readable for future-me.</li>
<li>There was also a point where I was trying to reinvent <code>&gt;&gt;=</code> by doing <code>join . fmap</code>, but chatGPT reminded me that I could simply use <code>&gt;&gt;=</code> instead. That made the code concise. I was thinking if I'd end up not understanding this code in the future but decided to keep it. It also got me thinking about <a href="https://mail.haskell.org/pipermail/haskell-cafe/2009-March/058475.html">this discussion</a> that I stumbled upon recently (one person argues Haskell style guides should recommend more simple/readable code, while another argues it's up to the developers to learn to read terse code).</li>
<li>The digest I produce is an HTML file with some styling. I generate this HTML file by using a template and then just swapping/replacing some parts of the template with relevant data. I learned how to bundle the template as part of the binary by inlining/embedding it using the <code>file-embed</code> package—all of this thanks to chatGPT.</li>
</ul>
]]></description>
<pubDate>Tue, 15 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 8</title>
<link>https://code.druchan.com/haskell-journal-day-8</link>
<guid>https://code.druchan.com/haskell-journal-day-8</guid>
<description><![CDATA[<ul>
<li>While I actually didn't want to work on the project today, one thing led to another and I ended up working on it.</li>
<li>I was watching <a href="https://www.youtube.com/watch?v=kbFGvUXqUcw&amp;pp=ygUSaGFza2VsbCBleGNlcHRpb25z">this video</a> on exceptions and realized I could use a simple <code>IO a</code> type for my functions instead of <code>ExceptT...</code> because all <code>IO a</code> types <em>can</em> potentially throw. Combined with a custom <code>Exception</code> instance for my <code>AppError</code>, I could potentially get rid of a few <code>runExceptT</code>s in the code — so I went about doing it and the whole app got converted into a simple <code>IO a</code> (i.e., the <code>App a</code> wrapper stopped being an <code>ExceptT ...</code> and became an <code>IO a</code>). There were some places where I had to handle things correctly so that errors were managed properly (like when trying to insert a link, the inner function crashes if the SQL insert query fails — but the handler that uses the inner function shouldn't crash, because failing for one feed item shouldn't fail others).</li>
<li>What I like about this approach is that I can have any throwable <code>IO a</code>, and I can simply run it through <code>failWith &lt;AppError&gt;</code> and it will produce an <code>IO a</code> or throw with the right kind of <code>AppError</code>. i.e, it takes an <code>IO a</code> that could throw a <code>SomeException</code> and converts it into an <code>IO a</code> that could throw an <code>AppError</code>.</li>
<li>The codebase became much cleaner and reasoning about the steps is now far simpler. I also discovered there was one place where I was returning an <code>IO a</code> for no reason — the function could be very pure and return <code>a</code> simply.</li>
<li>After this, I couldn't stop. I ended up writing a simple command system for the app so I can now compile the binary and run a few commands like:</li>
</ul>
<pre><code class="language-bash">&gt; rss-digest add &lt;url&gt; # this adds an XML URL to the database, correctly showing an error if the URL is already added or it's an invalid URL.
&gt; rss-digest refresh # this fetches all feed links from all the RSS feeds in the database, and then updates the feed_items table.
&gt; rss-digest purge # nukes the whole thing
</code></pre>
<ul>
<li>I wanted to check the size of the binary and it was a whopping 64MB. Ignorant me was surprised as heck. I asked ChatGPT about it and it said it was because the binary was packing everything to be self-sufficient. One of the options it gave was to use dynamic linking instead of static linking — I had concerns about this because how would that work if one were to distribute the binary? The reduction was anyway not all that great. There were other options it suggested like &quot;no profiling&quot;, turning off debug mode, using some &quot;O2&quot; mode of optimization etc... nothing really worked.</li>
<li>One thing I missed in the ChatGPT list of recommendations was to use the <code>strip</code> command on the binary. Instead, I went to Stack Overflow and Google... and one of the suggestions there was <code>strip</code> too. So I did that and the binary came down to 41MB which is still humongous (for comparison, GitHub's CLI tool <code>gh</code> v2.49 is ~48MB). <code>strip</code> removes all the symbols from the binary. I did a test run of the binary after doing the <code>strip</code> and it was working OK. (update: if I compile the binary with dynamic linking, and then do a <code>strip</code>, the binary size is about 200kb)</li>
<li>I'm quite happy about the way the project has shaped up so far. Might do a recap of what I learned, patterns that seem to emerge etc at some point.</li>
</ul>
<p>Update:</p>
<ul>
<li>I did some more work as I was bored and couldn't stop being obsessed with the project for a bit.</li>
<li>I wrote a Makefile because I was frequently running <code>cabal build ...</code> and <code>cabal install</code>. This made the process quicker.</li>
<li>I found a problem where, when I added more constructors to my <code>Command</code> data type, the compiler didn't warn about missing pattern matches in the <code>main</code> function where I was handling the command. It turns out I had to enable some flags (specifically <code>-Wincomplete-patterns</code>). I ended up using <code>-Wall</code> and fixing a bunch of lint warnings like unused declarations and imports and adding type annotations where I hadn't written them out explicitly. This had zero impact on the built binary size, though.</li>
<li>I added a couple of commands to remove a feed and list all existing feeds. The remove feed command made me realize that I need to set up a foreign key relationship between the feeds_table and feeds — so that when I remove a feed, I also remove all the posts that came from that feed URL. I've noted this for later implementation.</li>
</ul>
]]></description>
<pubDate>Mon, 14 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 7</title>
<link>https://code.druchan.com/haskell-journal-day-7</link>
<guid>https://code.druchan.com/haskell-journal-day-7</guid>
<description><![CDATA[<ul>
<li>Continuing from <a href="./haskell-journal-day-6">day 6</a>'s thought, I put all the code (except the one-time Youtube feed link extractor) in a single file. While it felt like I had written a lot of code, it's just about 250 lines including some 25 lines of <code>import</code> statements. At some point in the project, I'll have to break this up but this is too early and unnecessary right now.</li>
<li>The biggest win for today has been -&gt; I got a good working prototype of a function that reads a table to get a list of rss links, then goes out to fetch each link and process it, and adds the posts from the feed to another table. And all of it done with the right kind of error handling (as far as my rudimentary tests are concerned). So now I could just invoke one function to fetch the latest updates from every rss link I have.</li>
<li>I had a terrible time gaining some understanding of the underlying monadic things about my data structure <code>App</code>. I played around a bunch of options — like reducing the lower-level functions to just be <code>IO a</code> instead of <code>ExceptT ...</code> etc but things kept feeling messy. I did finally manage to keep them all neat and tidy into the <code>ExceptT</code> (because that's how one can safely hold errors). Briefly, my app kept crashing if a malformed URL was sent down the wire and I realized it was just a matter of me not handling a throwable <code>IO ()</code> in a function that fetched contents of a URL.</li>
<li>Functions that use the connection pool (a.k.a functions that interact with the DB) were the hardest to write because the <code>withResource</code> function kept tripping me up. That function has type <code>Pool Connection -&gt; (Connection -&gt; IO a) -&gt; IO a</code> , i.e. it takes a connection pool and a function that takes a connection and returns an <code>IO a</code>... but it took my stupid brain a long time to figure out how to bubble errors up from the inner function. Was a simple <code>try $</code> slapped in front .. but I had to make sure the <code>try</code>'s <code>SomeException</code> was returned as <code>AppError</code> afterwards. This piece was the one that actually caused all sorts of trouble in my intuition of the app's helper functions (involving database access), but once this clicked in place, a lot of things got simplified.</li>
<li>Type-level reasoning saved the day a few times: I would write the <code>withResource</code> line, type annotate it, then figure out the inner function slowly by unwrapping and then wrapping the results...</li>
<li>I'd start with something like this:</li>
</ul>
<pre><code class="language-haskell">...
res &lt;- try $ withResource connPool handleSomething :: IO (...)
...
where
  handleSomething :: IO ... -- this is where I play with and finalze the type till compiler stops complaining
  handleSomething = undefined
</code></pre>
<ul>
<li>And then workout the <code>handleSomething</code> function by unwrapping/wrapping stuff.</li>
<li>With one of the core prerequisites done, I am now going to do some work on finding out what I want the outputs to look like and how the CLI should behave. I am partly leaning towards being able to run a single command that produces a simple HTML file which I can just serve or see directly to get my &quot;daily digest&quot;. But how would I mark the reads as reads if it's a static html file?</li>
<li>Update: at the end of the day, I spent a little more time on the codebase. I was particularly looking for ways to extract some patterns out and minimize code. Somewhere, I feel like there are a bunch of utilities from the standard library that I could be using to wrap, unwrap, map over the monadic datatypes involved in the app at this point, but I couldn't really get a sense of what those would be. (The <code>hlint</code> does sometimes suggest interesting alternative options that make the code concise, without losing the readability mostly). I did realize though that I have a bunch of <code>try</code>s in the app and then I always have to handle the <code>SomeException</code> and convert it into my <code>AppError</code>... so I wrote a custom <code>try'</code> that I could use all over the place and never worry about having to convert <code>SomeException</code> to <code>AppError</code> again.</li>
</ul>
<pre><code class="language-haskell">try' :: (String -&gt; AppError) -&gt; IO a -&gt; IO (Either AppError a)
try' mkError action = do
  res &lt;- (try :: IO a -&gt; IO (Either SomeException a)) action
  pure $ case res of
    Left e -&gt; Left . mkError $ show e
    Right a -&gt; Right a
</code></pre>
]]></description>
<pubDate>Sun, 13 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 6</title>
<link>https://code.druchan.com/haskell-journal-day-6</link>
<guid>https://code.druchan.com/haskell-journal-day-6</guid>
<description><![CDATA[<ul>
<li>Managed to remove the complexities of the <code>ReaderT</code> thingy with the app and introduced a much simpler type:</li>
</ul>
<pre><code class="language-haskell">type App a = Config -&gt; ExceptT AppError IO a
</code></pre>
<ul>
<li>So now, any function that needs the config can simply be a function that takes a <code>Config</code> and returns an <code>ExceptT AppError IO a</code>.</li>
<li>I also updated the feed-item extracting part of the code so that it can, in one pass, work for both Youtube feeds and other RSS XML 2.0 spec feeds — the difference in YT feeds is that they use <code>&lt;link href=&quot;...&quot; /&gt;</code> instead of <code>&lt;link&gt;{actual_url}&lt;/link&gt;</code>, and they use <code>&lt;published&gt;</code> and <code>&lt;updated&gt;</code> instead of <code>&lt;pubDate&gt;</code>.. basically different specs. The way I handled this was to use the wonderful <code>&lt;|&gt;</code> (alternative) operator. My use-case has been very simple so far thankfully — I just try to extract/parse (using <code>tagsoup</code>) and then return a <code>Maybe String</code>. Combined with the <code>&lt;|&gt;</code> operator, I get what I want - if the item has <code>&lt;pubDate&gt;</code>, that's extracted and returned. But if it has <code>&lt;updated&gt;</code> instead, that will be extracted and sent. If both exist, the first will be returned.</li>
</ul>
<pre><code class="language-haskell">extractData :: [Tag String] -&gt; FeedItem
extractData tags =
  let title = getInnerText $ takeBetween &quot;&lt;title&gt;&quot; &quot;&lt;/title&gt;&quot; tags
      linkFromYtFeed = extractLinkHref tags -- youtube specific
      link = case getInnerText $ takeBetween &quot;&lt;link&gt;&quot; &quot;&lt;/link&gt;&quot; tags of
        &quot;&quot; -&gt; Nothing
        x -&gt; Just x
      pubDate = case getInnerText $ takeBetween &quot;&lt;pubDate&gt;&quot; &quot;&lt;/pubDate&gt;&quot; tags of
        &quot;&quot; -&gt; Nothing
        x -&gt; Just x
      updatedDate = case getInnerText $ takeBetween &quot;&lt;updated&gt;&quot; &quot;&lt;/updated&gt;&quot; tags of
        &quot;&quot; -&gt; Nothing
        x -&gt; Just x
      updated = pubDate &lt;|&gt; updatedDate
   in FeedItem {title = title, link = link &lt;|&gt; linkFromYtFeed, updated = fromMaybe &quot;&quot; updated}
</code></pre>
<ul>
<li>The success in <code>extractData</code> did not last by the time it came to working with functions that had to interface with the DB. Had a lot of trouble writing that one function <code>processFeed :: URL -&gt; App ()</code> which takes a URL (String), and an app config (<code>Config</code>) and then actually fetches the contents, then writes to the database. This was because I had a tough time unwrapping/wrapping to <code>IO ()</code> without losing the errors.. but then, I realized I had not really thought about error/crash strategy: like, when I extract feed items and attempt to write each link to the database, should I crash at the first error or should I just log it to the console and carry on with the next item?</li>
<li>I decided that it was best to log and continue to the next. Then came the question of which function should do the logging? For now, the <code>insertFeedItem</code> (which takes a feed item and writes to the DB) is the one that logs... but that came about after so much wrangling with these functions. At the end of the day, I went with really simplified <code>insertFeedItem</code> whose return type went from <code>ExceptT ... ()</code> to <code>IO ()</code>... which I am not really happy about because a thrown error is not caught.</li>
<li>Eventually, <code>insertFeedItem</code> got wrapped inside <code>processFeed</code> (which processes a single feed URL), which then got wrapped inside <code>processFeeds</code> which processes a list of URLs, simply mapping over the <code>processFeed</code> function.</li>
<li>Overall — the good thing is that I managed to get a working function that took a URL and fed the feed items into the table. But the code is already somewhat confusing and there were times where things compiled and worked but I just couldn't get a complete understanding of what was going on in the wrapping/unwrapping — a feeling I commonly encounter when dealing with these functional programming languages.</li>
<li>I spent time on finalizing the table schemas. The <code>link</code> is the primary key so duplicate inserts are prevented at the DB level besides being prevented at the code level.</li>
<li>I am also briefly mulling if I could put all the code in a single file. I have modules around functions (like DB-related, RSS parsing related etc) but they are already coupled by sharing of types and whatnot. So I am thinking maybe for now I will put them all in a single Main.hs file and then extract out modules based on what natural grouping requirements emerge in that file.</li>
</ul>
]]></description>
<pubDate>Sat, 12 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 5</title>
<link>https://code.druchan.com/haskell-journal-day-5</link>
<guid>https://code.druchan.com/haskell-journal-day-5</guid>
<description><![CDATA[<ul>
<li>Quite possibly one of the worst days in the project so far.</li>
<li>At first, I set up the app to be an <code>AppM</code> monad that was basically a <code>ReaderT config (ExceptT SomeException IO) a</code> — continuing from the idea from <a href="/haskell-day-4">day 4</a>. It was interesting and somewhat simple to execute because I had backing code from ChatGPT, and a fairly decent understanding of what was happening with the <code>runApp</code> mechanics. I had a <code>runApp</code> function that took a function of type <code>AppM a</code>, and then just ran it by passing it config like so:</li>
</ul>
<pre><code class="language-haskell">runApp :: AppM a -&gt; IO (Either e a)
runApp app = do
	config &lt;- ask -- gets the global app config
	runExceptT $ (runReaderT app config)
	-- runReaderT runs the app and extracts the inner monad (ExceptT e a)
	-- runExceptT then runs the inner monad and returns an IO (Either e a)
</code></pre>
<ul>
<li>Problems started brewing because all my lower-level functions were of the <code>ExceptT</code> type. At some point, the way I was doing it, running the app returned something like <code>IO (Either SomeException (Either SomeException ()))</code> — i.e., I was not passing the ExceptT context correctly.</li>
<li>This had disastrous effects: errors were not being bubbled up (in my mind, that was the whole point of wrapping the app in the ExceptT monad). Instead, they were wrapped in another Either monad surrounded by the IO. Horrible. I had output that looked like this:</li>
</ul>
<pre><code class="language-haskell">Right (Left &lt;some_error_message&gt;)
</code></pre>
<ul>
<li>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 <code>runExceptT</code> 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.</li>
<li>But it did tell me how to actually unwrap the ExceptTs at lower-levels (using <code>runExceptT</code> of course), and then lift them in the <code>AppM</code> monad so that the exceptions bubble up and are caught at the top-most function — which is exactly what I want but not at the cost of having to run <code>runExceptT</code>s at the inner functions and then lifting them using custom lift functions.</li>
<li>I had working code that did involve <code>runExceptT</code> and an interesting custom <code>liftEitherAppM</code> function:</li>
</ul>
<pre><code class="language-haskell">liftEitherAppM :: Either SomeException a -&gt; AppM a
liftEitherAppM = either throwE return . lift
</code></pre>
<ul>
<li>As you can imagine, I spent all this time trying to understand some higher-level mechanics of the app (i.e. abstraction) rather than just working on the actual functionality and it was too late by the time I realized what I was doing.</li>
<li>One other interesting thing that I noticed was that I did not google for solutions or ideas for a long time; it was almost at the end that I realized I could also just google/SO for solutions. That didn't help though. Ended up posting my questions on the FP slack.</li>
</ul>
<p>Update: In the morning, I had the idea of looking at opensource Haskell projects to see what patterns they use. I tried Hakyll and PostgREST: both the projects seem to be just passing config to the relevant functions, so I think I will simplify the app for now to just pass <code>Config</code> (the one that has connection pool so that my inner functions that deal with DB can get a connection to work with) to the functions instead of using the <code>ReaderT</code> monad transformer until I get some more ideas and understanding of the monad.</p>
]]></description>
<pubDate>Fri, 11 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 4</title>
<link>https://code.druchan.com/haskell-journal-day-4</link>
<guid>https://code.druchan.com/haskell-journal-day-4</guid>
<description><![CDATA[<ul>
<li>A couple of big wins today.</li>
<li>The big-ticket item: I managed to port most functions to <code>ExceptT</code>, so now I don’t have to wrestle with the <code>IO (Either e a)</code> datatype with pattern matching or bifunctor wrangling. Most functions involving side effects are now just <code>ExceptT SomeException IO a</code>—I can use them as if they yield the happy-path result. At some point, a <code>main</code> function will run <code>runExceptT</code>, and I can handle the errors there.</li>
<li>Another big-ticket item: I got a handle (no pun intended) on integrating SQLite into my project and was able to write into one of the tables. All exploratory, but it worked—I wrote data into the DB from a Haskell function, and it landed safely in the SQLite file. I also started thinking about the table schema, but I haven't spent enough time on that yet.</li>
<li>Since it's still early, I haven’t thought about managing the database and changes—so no migrations yet. I just nuke the database when I need to change the columns or table structure.</li>
<li>I was happy that if a table has a <code>TEXT</code> column (with a <code>NOT NULL</code> constraint), passing a <code>Just String</code> value inserts the <code>String</code> into the column, but passing <code>Nothing</code> throws an error at the DB layer.</li>
<li>With the database integrated, two new problems arose: every function interfacing with the DB has to open and close the connection, and passing the connection around is redundant and ugly. I knew I'd eventually have to dip my toes into the <code>ReaderT</code> monad... turns out I’ll have to do that soon.</li>
<li>To wrap up the day, I chatted with ChatGPT to check some examples on how to combine <code>ReaderT</code> (to pass global config, like the DB connection) and <code>ExceptT</code> to handle errors gracefully. The examples were simple enough, so I have my work cut out for the next day on this project.</li>
</ul>
]]></description>
<pubDate>Thu, 10 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 3</title>
<link>https://code.druchan.com/haskell-journal-day-3</link>
<guid>https://code.druchan.com/haskell-journal-day-3</guid>
<description><![CDATA[<ul>
<li>I reflected on the project so far—it fetches RSS links for all the channels I'm subscribed to. But I could pivot and make this into a tool that works more like an RSS feed reader, or better yet, a way for me to get a &quot;daily digest&quot; from all the websites I want to track (using their RSS feeds).</li>
<li>This idea sounds great because it allows me to play with databases as well. I’d have to store the data in a DB (leaning towards SQLite), and then fetch the &quot;daily digest&quot; data from there.</li>
<li>This meant I needed to parse XML from RSS feeds and extract at least the following: <strong>title</strong>, <strong>original link of the post</strong>, and <strong>published or updated date</strong>.</li>
<li>After looking around, I found <strong>TagSoup</strong> to be the best option (Scalpel didn't seem like the right fit since it's more focused on HTML than XML).</li>
<li>I asked ChatGPT for an introduction to <strong>TagSoup</strong>, which gave me an idea of the main functions to use. Armed with this info, I dove into the documentation and found a few more helpful bits.</li>
<li>It took a few tries to get things right. I created a dummy XML file to test on, and I had to extract the title, link, and updated date. <strong>TagSoup</strong> has some nice operators and combinators. After about an hour, I managed to extract all feed items from the given XML (though now I realize it’s specific to YouTube's RSS feed—other feeds have different tags for their items… this will be another problem to solve).</li>
<li>The tool has now morphed—it no longer extracts feed links from YouTube channel URLs. I removed that functionality since it was a one-time need for me (specific to YouTube channel feeds).</li>
<li>I'm now thinking of refocusing the tool to perform tasks like:
<ul>
<li>Adding a feed to the database (<code>./app --add-feed &lt;feed_url&gt; --other-args</code>).</li>
<li>Running and fetching the daily digest (<code>./app digest</code>).</li>
<li>Managing the feeds stored in the DB with additional commands.</li>
</ul>
</li>
<li>Another thought: I used <strong>Scalpel</strong> for extracting info from YouTube subscriptions and for fetching the RSS feed link. I could potentially use <strong>TagSoup</strong> and <strong>html-conduit</strong> to handle these tasks and remove <strong>Scalpel</strong> as a dependency. However, since the YouTube-specific code is likely one-time use, this might not be necessary.</li>
<li>Another discovery today, though I haven't fully explored it, is the use of <strong>EitherT</strong> and <strong>ExceptT</strong> monad transformers, which could simplify handling <code>IO (Either e a)</code> types. I had used these in my PureScript project (which manages my blog) but had forgotten about them. I asked ChatGPT, and it reminded me of monad transformers.</li>
<li>Day 3 has opened up a lot of new possibilities for coding!</li>
</ul>
]]></description>
<pubDate>Wed, 09 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 2</title>
<link>https://code.druchan.com/haskell-journal-day-2</link>
<guid>https://code.druchan.com/haskell-journal-day-2</guid>
<description><![CDATA[<ul>
<li>The tool initially handled a single URL and returned the feed link, but I needed it to work with multiple URLs since I had a lot of YouTube channels whose RSS feed links I wanted.</li>
<li>The easy solution: pass a file to the tool, where the file contains one YouTube channel link per line. The code would then process each link, fetch the feed link, and finally write all the results to another file.</li>
<li>This was straightforward to implement—I simply used <code>mapM</code> over the existing function that scraped and extracted the feed link.</li>
<li>I asked ChatGPT if there was a more efficient, concurrent way to perform the mapping instead of <code>mapM</code>, and it suggested <code>mapConcurrently</code> from the <code>async</code> library. I added it and tested the execution times. However, there wasn't much difference between <code>mapM</code> and <code>mapConcurrently</code>. I suspect I might not be using it optimally.</li>
<li>I spent some time experimenting with extracting additional data (such as the channel title, avatar, etc.), but I ultimately decided to stick to just extracting the feed link since this was intended to be a one-time operation.</li>
<li>I realized that I wasn't handling file I/O errors properly when reading the list of URLs or writing the feed links. The code was just performing <code>IO a</code>, which could crash if there was an issue (e.g., an invalid file path). I decided to look into using <code>try</code> from <code>Control.Exception</code>—something I had used before in Purescript.</li>
<li>I struggled with how to handle the exceptions and where to handle them. The types were now <code>IO (Either SomeException a)</code>, and I had to write several pattern matches (inside <code>do</code> blocks) for the <code>Left</code> and <code>Right</code> cases of the <code>Either</code>. I asked ChatGPT for ways to reduce this boilerplate, and it suggested using the <code>either</code> function, which wasn't as helpful as I hoped. I decided to revisit this later.</li>
<li>I tend to trip up when dealing with <code>IO (Either ...)</code> types because I can't always tell where the code is in the <code>IO</code> context versus the <code>Either</code> context. My current approach involves trying every combination until the compiler stops complaining.</li>
<li>After setting up the functions and testing them, I could finally implement the final feature—getting the tool to accept a <code>--path</code> parameter instead of a <code>--url</code>. This involved adding another pattern match in the code for <code>(&quot;--url&quot; : url : _)</code>, but also required differentiating between a URL and a file path. A custom <code>data</code> type for the arguments helped here.</li>
<li>Day 2 was interesting—it made me realize that I need to build error handling into my data types and functions from the start. The <code>IO (Either e a)</code> type is not ideal; I need an abstraction over it. The tool can now handle both single URLs and a file path containing multiple URLs, and it writes the feed links to a file.</li>
</ul>
]]></description>
<pubDate>Tue, 08 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Haskell Journal - Day 1</title>
<link>https://code.druchan.com/haskell-journal-day-1</link>
<guid>https://code.druchan.com/haskell-journal-day-1</guid>
<description><![CDATA[<ul>
<li>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, &quot;Yes, I can build that in Haskell.&quot;</li>
<li>Areas to focus on:
<ul>
<li>File I/O</li>
<li>Making API calls to some service and processing data</li>
<li>Web scraping</li>
<li>Running a web server</li>
<li>Interacting with a database</li>
<li>Concurrency</li>
</ul>
</li>
<li>A recent problem I faced: YouTube's UI doesn't expose RSS feeds for channels, but it's present in the HTML source code. The idea: a CLI tool that takes a YouTube channel URL, scrapes the HTML source, and returns the feed link.</li>
<li>Asked ChatGPT for a good web scraping library in Haskell. It suggested <code>tagsoup</code> and <code>html-conduit</code> (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 <code>scalpel</code>. Liked it, and decided to use it.</li>
<li>Wrote some initial code based on ChatGPT's example of scraping. However, ChatGPT got the <code>attr</code> function wrong. There was some wrangling between <code>ScraperT</code> and <code>Scraper</code>, and I finally learned that I could use the <code>Identity</code> monad to go from <code>ScraperT</code> to <code>Scraper</code>. (Monad Transformers).</li>
<li>Also experimented with constructing a scraper from a selector and then using it in the <code>scrapeURL</code> function.</li>
<li>It took me a while to get an intuition for how the selectors and scrapers fit together, and then using the scraper in the scrape runners. I also felt uneasy when trying to &quot;compose&quot; a scraper from other scrapers (almost thought it was impossible).
<ul>
<li>I hit this issue because I needed to scrape the whole source but pick multiple data points—title of the channel and feed link URL—which were in different tags.</li>
<li>The solution was to use <code>optional</code>.</li>
<li>Eventually, I didn’t need this.</li>
</ul>
</li>
<li>The final piece of the puzzle was running this as a CLI tool, meaning I needed to take an argument (the YouTube channel's URL). ChatGPT suggested <code>optparse-applicative</code>, but I decided it was overkill for what I needed. A simple <code>--url &lt;url&gt;</code> argument was enough. ChatGPT suggested pattern matching on the arguments:</li>
</ul>
<pre><code class="language-haskell">case args of
  (&quot;--url&quot; : url : _) -&gt; ...
  _ -&gt; ...
</code></pre>
<ul>
<li>I tried to initialize the project with Stack first, but it threw some weird errors. So I decided to go with a Cabal-only approach, which worked. I haven’t investigated why Stack failed. I might not do that for now, as the Cabal setup seems good enough—I was able to add packages, build the binary, test it, and also run the REPL from Cabal.</li>
<li>Overall, a great day 1—just the right amount of pushing against my comfort zone.</li>
</ul>
]]></description>
<pubDate>Mon, 07 Oct 2024 12:00:00 +0530</pubDate>
</item><item>
<title>Going from Promises to Aff in Purescript</title>
<link>https://code.druchan.com/js-promise-to-purescript-aff-ffi</link>
<guid>https://code.druchan.com/js-promise-to-purescript-aff-ffi</guid>
<description><![CDATA[<p>The other day, I was fooling around with an idea that has come up often in my chats with <a href="https://en.wikipedia.org/wiki/Jon_Udell">Jon Udell</a> at work. We do not have a good test suite that can verify and flag errors in the hundreds / thousands of example queries listed on <a href="https://hub.steampipe.io">Steampipe Hub</a> and – since a lot many weeks had passed since I wrote some program for fun – I decided to take a shot at this. I chose Purescript.</p>
<p>Very briefly, the idea is to:</p>
<ul>
<li>&quot;extract&quot; the example queries from where they live (either in a markdown file in between <code>sql</code> codeblocks or in a configuration file) in a plugin or a mod repository and,</li>
<li>run those queries using <a href="https://steampipe.io">steampipe</a> and,</li>
<li>collect and log the results.</li>
</ul>
<p>Needless to point out, this involved running some CLI commands and reading files etc. That is to say, a bunch of <code>Node.*</code> modules in Purescript.</p>
<p>At first, I wrote the thing to run sequentially. This is slow but for development and quick prototyping, this was OK. This meant I could get away with <code>Effect</code> monads all the way through. Most notably, <code>Node.ChildProcess</code>'s <code>execSync</code> was very handy to run commands and grab the results without having to fall into callback traps.</p>
<p>The first draft ran painfully slowly because it was running validation checks on some dozen queries in series/sequence and that took a handful of minutes. The validation and logging worked – great – but so many minutes to check just <em>one</em> plugin? That won't fly. I had to parallelize it now.</p>
<p><a href="https://pursuit.purescript.org/packages/purescript-parallel/7.0.0/docs/Control.Parallel#v:parTraverse"><code>parTraverse</code></a> is a go-to for parallelizing <code>traverse</code> (and the equivalent for <code>sequence</code> is <code>parSequence</code>) but the big problem I had at this point was that all my &quot;effect-ful&quot; functions were in the <code>Effect</code> monad... and that monad has no <code>Parallel</code> instance. (To the uninitiated, your monad needs a <code>Parallel</code> instance to be able to use functions like <code>parTraverse</code> on it).</p>
<p>This meant I had to convert all those <code>Effect</code> monads into <code>Aff</code> monads.</p>
<p>And that's where converting <code>execSync</code> to <code>Aff</code> took me on a goose-chase.</p>
<p>Obviously, the first thing I tried to ChatGPT and Google was &quot;how to convert an <code>Effect</code> to <code>Aff</code>&quot; and the only standard library function to do this is the seemingly-complicated <a href="https://pursuit.purescript.org/packages/purescript-aff/7.1.0/docs/Effect.Aff#v:makeAff"><code>makeAff</code></a> function. I've yet to wrap my mind around that.</p>
<p>The other thing to try involved <a href="https://book.purescript.org/chapter10.html">FFI</a> with <a href="https://pursuit.purescript.org/packages/purescript-aff/5.1.2/docs/Effect.Aff.Compat#t:EffectFnAff"><code>EffectFnAff</code></a>. This seems straightforward at first and probably is to people who have successfully intuited it, but it mandates your foreign function to be in a particular shape.</p>
<p>In both the <code>makeAff</code> and <code>EffectFnAff</code> cases, I was able to get the code to compile (which is a great sign that your code works in Purescript) but the query validation continued to run in sequence instead of parallel.</p>
<p>Turns out JS's <code>execSync</code>, even when converted into a promise on the JS side and then imported into an <code>Aff</code> on Purescript via <code>EffectFnAff</code> will continue to block. (That or the way I wrote it was blocking).</p>
<p>Finally, I stumbled on <code>aff-promise</code> through <a href="https://blog.drewolson.org/purescript-async-ffi">this post</a> and discovered a very simple way to convert Node's <code>exec</code> into a Purescript <code>Aff</code>:</p>
<p>On the JS side, you have this:</p>
<pre><code class="language-js">import { exec } from &quot;child_process&quot;;

export const exec_ = (cmd) =&gt; {
  return () =&gt; {
    return new Promise((res, rej) =&gt; {
      exec(cmd, { encoding: &quot;utf-8&quot; }, (err, stdout, stderr) =&gt; {
        if (err || stderr) {
          rej(err || stderr);
        } else {
          res(stdout);
        }
      });
    });
  };
};
</code></pre>
<p>which is essentially exporting a function that returns a &quot;thunk&quot; which returns a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a>.</p>
<p>And on the Purescript side, where you import this &quot;exec_&quot; as an FFI, you have this:</p>
<pre><code class="language-haskell">import Control.Promise (Promise, toAffE)
import Data.Either (Either)
import Effect (Effect)
import Effect.Aff (Aff, try)

foreign import exec_ :: String -&gt; Effect (Promise String)

execAff :: String -&gt; Aff (Either Error String)
execAff = try &lt;&lt;&lt; toAffE &lt;&lt;&lt; exec_
</code></pre>
<p>which is essentially:</p>
<ul>
<li>import the foreign/JS function <code>exec_</code> as an <code>Effect (Promise a)</code> (where <code>a = String</code> in my case)</li>
<li>convert the <code>exec_</code> result into an <code>Aff</code> using <code>toAffE</code> (so, <code>toAffE &lt;&lt;&lt; exec_ :: Aff String</code>)</li>
<li>slap a <code>try</code> onto this to catch any errors that could be thrown in the <code>exec_</code> function.</li>
</ul>
<p>I must've spent as much time on finding a way to convert <code>execSync</code> into an <code>Aff</code> as I did writing the entire program, but c'est la vie.</p>
<p>I hope to move to the query extraction part – where you point the script to a folder and the program extracts all queries it finds in the markdown or config files – in the next iteration.</p>
]]></description>
<pubDate>Tue, 28 Nov 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Building mobile apps using Elm and Capacitor</title>
<link>https://code.druchan.com/elm-capacitor</link>
<guid>https://code.druchan.com/elm-capacitor</guid>
<description><![CDATA[<p>Native mobile app dev is still largely object-oriented driven (Kotlin, Java etc). Languages like Kotlin now have a lot of functional programming paradigms supported out of the box (e.g. Arrow).</p>
<p>About two years ago, I wanted to see if <a href="https://elm-lang.com">Elm</a> can be used to build a meaningful Android app.</p>
<p>I manage my expenses on a simple Google spreadsheet. When I'm on the move, I'd jot down the expenses as notes and then type them out on the spreadsheet once I had access to my laptop. I wanted a way to add expenses directly to my spreadsheet through my phone. The Google Sheets mobile app does not offer a great UX for this.</p>
<p>I ended up building a (highly-personalized) <a href="https://github.com/chandru89new/xpns">expense tracking Android app using Elm</a>. I've been using this app ever since and has hardly needed a few updates in all this time.</p>
<p>Recently, I updated the &quot;bootstrap&quot; repo that I use to build Elm-based Android apps. (These can be used to build iOS apps as well). <a href="https://github.com/chandru89new/elm-capacitor-bootstrap">You can grab the source-code / clone the repo from here</a>.</p>
<p>But if you're interested in setting up an Elm-based Android app project yourself, here's my notes:</p>
<ol>
<li>Core dependencies</li>
<li>Bundling logic – how is the project built?</li>
</ol>
<h4>1. Core dependencies</h4>
<p>For this project, I use CapacitorJS. <strong>This means the whole application runs inside a web-view.</strong></p>
<p>Things have improved quite a bit in the web-view (and mobile browser engine) space in the recent years so building apps that run on web-views is not really a bad thing now.</p>
<p>For styling, I use <a href="https://tailwindcss.com/">TailwindCSS</a>. It's simple, clean and has one of the best styling ecosystems that one can ask for.</p>
<p>Custom JS glue-code has to be written to make your Elm app interact with native things (via CapacitorJS) and you'd ideally write these in ES6 or later. So, the app would need a way to be &quot;transpiled&quot; and/or bundled. To do this, I use <a href="https://parceljs.org">Parcel</a>. Not exactly the <em>best</em> solution out there but it's far more than enough for a bootstrap. Eventually, I'd like to swap this out with <a href="https://vitejs.dev/">Vite</a> to see how things work.</p>
<h4>2. Bundling logic – How is the project built?</h4>
<p>If we were building an Elm app, this is broadly the workflow:</p>
<ul>
<li>write Elm code</li>
<li>compile Elm to JS</li>
<li>include the compiled JS in an <code>index.html</code> file and init the Elm app via <code>Elm.Main.init()</code></li>
<li>serve the <code>html</code> file and JS assets.</li>
</ul>
<p>Building a mobile app is <em>almost</em> the same, except for a couple of steps:</p>
<ul>
<li>include Capacitor-JS related code (in Javascript) – typically, Elm and Capacitor would communicate via ports,</li>
<li>and because Capacitor-related code will end up being ES6 or later, use a bundler like Parcel</li>
</ul>
<p>The bootstrap's project structure is simple:</p>
<pre><code>public
├── css
│   ├── index.css
│   └── style.css
├── index.html
└── js
    ├── elm.js
    └── index.js
</code></pre>
<p>Here's more info:</p>
<pre><code>public
├── css
│   ├── index.css &lt;-- all your custom CSS goes here
│   └── style.css &lt;-- this file gets auto-generated by the `yarn build` command
├── index.html &lt;-- main entry-point for the project. Parcel will use this file to build/bundle the project.
└── js
    ├── elm.js &lt;-- this file is auto-generated by Elm during the `yarn build` process
    └── index.js &lt;-- all your custom Capacitor / other JS can go in this file (and other JS files)
</code></pre>
<p>The build step does these things:</p>
<ul>
<li>it compiles Elm code to JS</li>
<li>it builds a minified CSS file</li>
<li>then it lets Parcel bundle the whole project into a separate directory (<code>web</code>)</li>
<li>and finally, it lets Capacitor &quot;<a href="https://capacitorjs.com/docs/cli/commands/sync">sync</a>&quot; the project which is basically Capacitor copying over the <code>web</code> into the Android/iOS project folder.</li>
</ul>
<h4>3. Caveats and explorations</h4>
<p>One of the things I realized while building the personal expense tracking app was that Elm routing doesn't work. So I had to resort to using <code>Browser.element</code> and using a custom <code>Page</code> type as part of the <code>Model</code>.</p>
<p>Elm ports <em>can</em> feel a little tedious to hook up with the Capacitor bridge. However, with some good <a href="https://github.com/chandru89new/harbor">abstractions</a>, using ports can be more streamlined.</p>
<p>Capacitor is not the only JS-native bridge. There are also other tools like <a href="https://github.com/NativeScript/NativeScript">NativeScript</a> and it could be worth exploring how that plays with an Elm project.</p>
]]></description>
<pubDate>Tue, 15 Aug 2023 12:00:00 +0530</pubDate>
</item><item>
<title>A Thousand Splendid Promises – Concurrency with Limits in Javascript</title>
<link>https://code.druchan.com/concurrent-limits</link>
<guid>https://code.druchan.com/concurrent-limits</guid>
<description><![CDATA[<p>Update: the solution is not quite what the problem expects. See if you can find the issue.</p>
<p>The other day, I stumbled on <a href="https://twitter.com/thdxr/status/1686856181745111040">this tweet</a>:</p>
<p><img src="/images/concurrent-promise-tweet.png" alt="tweet"></p>
<p>While the tweet does say <em>libraries allowed</em>, it got me curious.</p>
<p>What if it said <em>no libraries allowed</em>?</p>
<p>There are possibly many <em>clever</em> ways of solving it. As I thought about it, I realized that this could be a great exercise to <strong>implement an actual concurrent promise executer that can be used for any kind of a list</strong>!</p>
<p>So here I am.</p>
<p>First off, let's scope out what we want to do:</p>
<ul>
<li>run a large list of promises/async functions parallelly</li>
<li>but run them in batches of X, where X = some integer</li>
<li>make sure to collect all errors</li>
<li>in fact, make sure to collect all values! that way, one can use this even if they want to extract all values out</li>
</ul>
<p>To be sure, I am not aiming for brevity or cleverness here. I'm looking to build the lego building blocks (primitives) that will help us &quot;compose&quot; or construct the final function easily.</p>
<h3>Breaking down the basic structure/idea of concurrency with limitations</h3>
<p>What does it mean to run a 1000 promises, but 25 at a time?</p>
<p>How can be break this down into smaller steps?</p>
<ul>
<li>First, split the 1000 items into lists of 25 each. That is, <em>make groups of X where X = 25</em>.</li>
<li>Then, loop through each <em>group</em> and run all the promises <em>inside</em> each group <em>parallelly</em>.</li>
<li>While doing that, make sure you <em>await</em> the result of each group's promise run before running the next. That is, <em>each group should run sequentially</em>.</li>
<li>Finally, flatten everything because we had <em>grouped</em> a giant list into a list of smaller lists. And return the flattened results.</li>
</ul>
<p>We need small functions/helpers to do each of these:</p>
<ul>
<li>we need a <code>groupsOf</code> function to split a large list into a list of smaller items,</li>
<li>we need a helper that can take a list of promises, run them parallelly and return the results,</li>
<li>and we need a helper that can take a list of promises and run them sequentially.</li>
</ul>
<h3>Groups of X</h3>
<pre><code class="language-js">const groupsOf =
  (number = 0) =&gt;
  (arr = []) =&gt; {
    return arr.reduce(
      (acc, curr, idx) =&gt; {
        const step_ = acc.step.concat(curr);
        if (idx === arr.length - 1 || step_.length === number) {
          return { final: acc.final.concat([step_]), step: [] };
        }
        return { final: acc.final, step: step_ };
      },
      { final: [], step: [] }
    ).final;
  };
</code></pre>
<p>The <code>groupsOf</code> function takes a number (the max number of items in a list), an array and then chunks the array into groups of whatever number we give it.</p>
<p>The logic is simple: it accumulates a <code>step</code> list till the number of items in the <code>step</code> list reaches the max number allowed. Once it reaches that, it pushes the <code>step</code> list into the <code>final</code> list and resets the <code>step</code> list. There are some checks to ensure that the if it's the last item in the array and the <code>step</code> list is not &quot;full&quot; yet, it still makes it to the <code>final</code> list.</p>
<p>Let's test this:</p>
<pre><code class="language-js">const array = range(1, 11);
console.log(groupsOf(3)(array));
// [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ], [ 10 ] ]
</code></pre>
<h3>Run promises parallelly and collect errors</h3>
<pre><code class="language-js">const runPromisesPar = async (promiseFns = []) =&gt; {
  return await Promise.allSettled(promiseFns.map((p) =&gt; p()));
};
</code></pre>
<p>Here, the <code>promiseFns</code> is a list of functions that return a promise.</p>
<p>So something like <code>async () =&gt; { return await something; }</code>.</p>
<p>This distinction is critical (as we'll use it again).</p>
<p>A promise is a value that could resolve or reject.</p>
<p>A promise function (in this post) refers to a function <em>that will return a promise when we call the function</em>.</p>
<p>So in our <code>runPromisesPar</code>, we take a list of functions that return a promise, do a <code>map</code> to <em>call</em> each function (so we have a list of promises) and use <code>Promise.allSettled</code> to convert it into an async list of values.</p>
<p>In types, we go from <code>Array&lt;Promise&lt;value&gt;&gt; -&gt; Promise&lt;Array&lt;value&gt;&gt;</code></p>
<p>We use <code>allSettled</code> instead of <code>all</code> because we want to &quot;collect&quot; errors. <code>all</code> would crash and return the first error it encounters. <code>allSettled</code> will run every promise even if there are errors/rejections and finally return all values/errors.</p>
<p>Testing this:</p>
<p>(I made up a few helper functions to create a promise function and then a list of promise functions):</p>
<pre><code class="language-js">const createPromise = (val, err, timeout = 100, idx) =&gt; {
  return () =&gt;
    new Promise((res, rej) =&gt; {
      console.log(
        `running promise #${idx} with val: ${val}, err: ${
          err ? err.toString() : null
        }, timeout: ${timeout}`
      );
      setTimeout(() =&gt; {
        if (val) {
          res(val);
        } else if (err) {
          rej(new Error(err));
        } else rej(&quot;No value or error given&quot;);
      }, timeout);
    });
};

const promises = range(1, 11).map((val) =&gt; {
  return createPromise(
    val % 5 === 0 ? null : val,
    val % 5 === 0 ? &quot;oops&quot; : null,
    val * 50,
    val
  );
});
</code></pre>
<pre><code class="language-js">&gt; console.log(await runPromisesPar(promises))

running promise #1 with val: 1, err: null, timeout: 50
running promise #2 with val: 2, err: null, timeout: 100
running promise #3 with val: 3, err: null, timeout: 150
running promise #4 with val: 4, err: null, timeout: 200
running promise #5 with val: null, err: oops, timeout: 250
running promise #6 with val: 6, err: null, timeout: 300
running promise #7 with val: 7, err: null, timeout: 350
running promise #8 with val: 8, err: null, timeout: 400
running promise #9 with val: 9, err: null, timeout: 450
running promise #10 with val: null, err: oops, timeout: 500
[
  { status: 'fulfilled', value: 1 },
  { status: 'fulfilled', value: 2 },
  { status: 'fulfilled', value: 3 },
  { status: 'fulfilled', value: 4 },
  {
    status: 'rejected',
    reason: Error: oops
        at Timeout._onTimeout (/Users/chandrashekharv/Documents/projects/promise-concurrency/test.js:21:15)
        at listOnTimeout (node:internal/timers:559:17)
        at processTimers (node:internal/timers:502:7)
  },
  { status: 'fulfilled', value: 6 },
  { status: 'fulfilled', value: 7 },
  { status: 'fulfilled', value: 8 },
  { status: 'fulfilled', value: 9 },
  {
    status: 'rejected',
    reason: Error: oops
        at Timeout._onTimeout (/Users/chandrashekharv/Documents/projects/promise-concurrency/test.js:21:15)
        at listOnTimeout (node:internal/timers:559:17)
        at processTimers (node:internal/timers:502:7)
  }
]
</code></pre>
<h3>Run promises sequentially</h3>
<pre><code class="language-js">const runPromisesSeq = async (promiseFns = []) =&gt; {
  let res = [];
  for (let promise of promiseFns) {
    res.push(await promise());
  }
  return res;
};
</code></pre>
<p>Nothing fancy here. We use a <code>for ... of ...</code> loop, <code>await</code> every promise and then proceed to the next one, collecting results all along.</p>
<p>Testing this:</p>
<pre><code class="language-js">&gt; console.log(await runPromisesSeq(promises))

running promise #1 with val: 1, err: null, timeout: 50
running promise #2 with val: 2, err: null, timeout: 100
running promise #3 with val: 3, err: null, timeout: 150
running promise #4 with val: 4, err: null, timeout: 200
running promise #5 with val: null, err: oops, timeout: 250
/Users/druchan/Documents/projects/promise-concurrency/test.js:21
          rej(new Error(err));
              ^

Error: oops
    at Timeout._onTimeout (/Users/druchan/Documents/projects/promise-concurrency/test.js:21:15)
    at listOnTimeout (node:internal/timers:559:17)
    at processTimers (node:internal/timers:502:7)
</code></pre>
<p>If there's an error in any promise, it will crash.</p>
<p>Why not &quot;handle&quot; this too?</p>
<p>Technically, we could but we don't have to, in our case. Our <code>runPromisesPar</code> returns a &quot;safe&quot; promise – one that will never crash. And we're only going to use the <code>runPromisesSeq</code> to run the groups returned from <code>runPromisesPar</code>.</p>
<p>Note: In a real-world setting, I'd probably make <code>runPromisesSeq</code> not crash but short-circuit and return the error as a value instead.</p>
<h3>Combining all these together</h3>
<pre><code class="language-js">const runPromiseConcurrent =
  (limit = 0) =&gt;
  async (promiseFns = []) =&gt; {
    // create the groups
    const promiseGroups = groupsOf(limit)(promiseFns);

    // promiseGroups is Array&lt;Array&lt;() =&gt; Promise&lt;any&gt;&gt;&gt;
    // we can only pass Array&lt;() =&gt; Promise&lt;any&gt;&gt; to `runPromisesSeq`
    // so we transform promiseGroups

    const transformed = promiseGroups.map(
      (group) =&gt; () =&gt; runPromisesPar(group)
    );
    // now transformed is Array&lt;() =&gt; Promise&lt;Array&lt;any&gt;&gt;&gt;
    // which is equivalent to Array&lt;() =&gt; Promise&lt;any&gt;&gt;

    // finally, run it and flatten the results
    return (await runPromisesSeq(promiseGroups)).reduce(
      (acc, curr) =&gt; acc.concat(curr),
      []
    );
  };
</code></pre>
<p>A simplified version:</p>
<pre><code class="language-js">const runPromiseConcurrent =
  (limit = 0) =&gt;
  async (promiseFns = []) =&gt; {
    const promiseGroups = groupsOf(limit)(promiseFns).map(
      (group) =&gt; async () =&gt; await runPromisesPar(group)
    );
    return (await runPromisesSeq(promiseGroups)).reduce(
      (acc, curr) =&gt; acc.concat(curr),
      []
    );
  };
</code></pre>
<p><a href="https://gist.github.com/chandru89new/1f8d7d299023a04b1384ee0b50610fe3#file-index-js">Here's a gist</a> of this all.</p>
]]></description>
<pubDate>Fri, 04 Aug 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Cellular Automata in Elm: Build Conway's Game of Life</title>
<link>https://code.druchan.com/conways-game-of-life-elm</link>
<guid>https://code.druchan.com/conways-game-of-life-elm</guid>
<description><![CDATA[<p>Cellular automata is a fun topic and Conway's Game of Life is a very popular cellular automaton.</p>
<p>In the previous Elm-specific post, I covered on <a href="https://dev.to/druchan/how-to-render-a-basic-calendar-ui-in-elm-hih">how to render a basic calendar</a>. In this one, let's write an application that implements Conway's Game of Life.</p>
<h2>Rules of the game</h2>
<p>Apparently there are many versions of <a href="https://en.wikipedia.org/wiki/Conway's_Game_of_Life">the &quot;game&quot;</a> but the basic rules are:</p>
<ul>
<li>a &quot;live&quot; cell will live in the next generation if it has exactly 2 or 3 &quot;live&quot; neighbors</li>
<li>a &quot;live&quot; cell will die if it has less than 2 or more than 3 &quot;live&quot; neighbors</li>
<li>a &quot;dead&quot; cell will come alive if it has exactly 3 &quot;live&quot; neighbors</li>
<li>a &quot;dead&quot; cell will remain dead if it has less than 3 neighbors.</li>
</ul>
<p>The trick for us is to figure out what it means to be a &quot;neighbor&quot; in the programmatic sense.</p>
<h2>What's a good data structure?</h2>
<p><strong>Conway's game of life is laid out as a grid containing lots of cells.</strong></p>
<p>For simplicity, we'll take a square grid. Example, a 15x15 grid will have 225 cells. We can start off with that.</p>
<p><strong>We need to know if a cell is alive or dead.</strong> So we could model a cell like this:</p>
<pre><code class="language-elm">type alias Cell = { status : Status }
type Status = Alive | Dead
</code></pre>
<p>But of course, we'd also need to know <strong>where that cell is located in the grid</strong> because we'd need to that to compute the neighbors...</p>
<pre><code class="language-elm">type alias Cell = { status : Status, position : Position }
type Status = Alive | Dead
type alias Position = (RowId, ColumnId)
type alias RowId = Int
type alias ColumnId = Int
</code></pre>
<p>That is, we just represent a cell as: a row index, a column index (these two locate the cell's position in the grid) and then the status which tells us whether the cell is alive or dead.</p>
<p>The grid is just a list of cells. So:</p>
<pre><code class="language-elm">type alias Grid = Array Cell
</code></pre>
<p>Why <code>Array</code> instead of <code>List</code>?</p>
<ul>
<li>Eventually, we'd need to work out the neighboring cells of a cell.</li>
<li>This involves filtering the cells.</li>
<li><code>Array</code>s are faster when it comes to such accesses.</li>
<li>Hence, using <code>Array</code> instead of <code>List</code>.</li>
</ul>
<h2>1. How to render a board?</h2>
<p>We have our types defined.</p>
<p>Let's now try and render a sample grid.</p>
<p>To get a sample grid, I'm going to use a <a href="https://package.elm-lang.org/packages/elm/random/latest/Random">random generator</a>.</p>
<h4>Generating a random Grid</h4>
<p>The logic is this:</p>
<ul>
<li>start with a &quot;random cell generator&quot; – takes a rowId, a columnId and returns a cell generator where the <code>status</code> could be dead or alive (with a 40/60 odds).</li>
<li>use this generator to create a Grid generator that can generate a list of cells when called</li>
</ul>
<p>First, the random cell generator:</p>
<pre><code class="language-elm">randomCellGenerator : RowId -&gt; ColumnId -&gt; Random.Generator Cell
randomCellGenerator rowId columnId =
    let position = (rowId, columnId)
    in
    Random.weighted (40, { position = position, status = Alive })
        [ (60, { position = position, status = Dead }) ]
</code></pre>
<p>Then, we use this in our Grid generator:</p>
<pre><code class="language-elm">randomGridGenerator : Int -&gt; Random.Generator Grid
randomGridGenerator size =
		-- create a List that starts with 1, and goes up to the size of the grid
    List.range 1 ((size * size))
    -- now map the list created above
    |&gt; List.map
            (\cellId -&gt;
                let
                    remainder =
                        remainderBy size cellId

                    rowId =
                        if remainder /= 0 then
                            (cellId // size) + 1

                        else
                            cellId // size

                    -- this creates the rowId
                    columnId =
                        if remainder == 0 then
                            size

                        else
                            remainder

                    -- this creates the columnId
                in
                randomCellGenerator rowId columnId
            )
    -- the above step returns a `List (Generator Cell)` but we need `Generator (List Cell)` so we `sequence` it. For this we use the `Random.Extra` package
    |&gt; Random.Extra.sequence
    -- and finally convert it into an Array.
    |&gt; Random.map (Array.fromList)
</code></pre>
<p>The random generator <code>Generator Grid</code> is not useful on its own.</p>
<p>We need to run the generator (so it generates the Grid) and for that, we need a <code>Msg</code>.</p>
<pre><code class="language-elm">type Msg
    = UpdateGrid Grid

type alias Model =
    { grid : Grid, size : Int }

update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )
update msg model =
    case msg of
        UpdateGrid grid -&gt;
            ({ model | grid = grid }, Cmd.none)

init : () -&gt; ( Model, Cmd Msg )
init _ =
    let
        size =
            15
    in
    ( { grid = Array.empty, size = size }, Random.generate UpdateGrid (randomGridGenerator size) )
</code></pre>
<p>In the <code>init</code> function, I'm generating a 15x15 grid.</p>
<h4>Rendering the Grid</h4>
<p>Again, simple composition here.</p>
<ul>
<li>First, we write a function that renders a cell.</li>
<li>Then, we use this to compose the function that writes an entire grid.</li>
<li>We'll use CSS grid for layout.</li>
</ul>
<p>The function that renders a cell:</p>
<pre><code class="language-elm">viewCell : Cell -&gt; Html Msg
viewCell { status, position } =
    case status of
        Alive -&gt;
            div
                [ Attr.style &quot;background-color&quot; &quot;black&quot;
                , Attr.style &quot;width&quot; &quot;16px&quot;
                , Attr.style &quot;height&quot; &quot;16px&quot;
                , Attr.style &quot;border&quot; &quot;1px solid black&quot;
                ]
                [ text &quot;&quot; ]

        Dead -&gt;
            div
                [ Attr.style &quot;background-color&quot; &quot;white&quot;
                , Attr.style &quot;width&quot; &quot;16px&quot;
                , Attr.style &quot;height&quot; &quot;16px&quot;
                , Attr.style &quot;border&quot; &quot;1px solid black&quot;
                ]
                [ text &quot;&quot; ]
</code></pre>
<p>Now, the grid renderer:</p>
<pre><code class="language-elm">viewGrid : Model -&gt; Html Msg
viewGrid { grid, size } =
    div
        [ Attr.style &quot;display&quot; &quot;grid&quot;
        , Attr.style &quot;gap&quot; &quot;0&quot;
        , Attr.style &quot;grid-template-columns&quot; (&quot;repeat(&quot; ++ String.fromInt size ++ &quot;,16px)&quot;)
        ]
        (Array.map viewCell grid |&gt; Array.toList)

-- simply map over the grid (which is an array) using the `viewCell` function, but convert that into a `List` because it's easier to deal with `List (Html Msg)` in view/render functions in Elm.
</code></pre>
<p>And finally:</p>
<pre><code class="language-elm">view : Model -&gt; Html Msg
view model =
    viewGrid model
</code></pre>
<p>If we <a href="https://ellie-app.com/nwB38nKQtR3a1">ran this application now</a>, we get something like this:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lv1ws311q2t16gh2zxx0.png" alt="grid render"></p>
<h2>2. Computing the &quot;next&quot; generation of the app</h2>
<p>Conway's game of life proceeds by moving to the &quot;next&quot; generation. Each &quot;step&quot; is going to the next generation.</p>
<p><strong>Next generation basically means which cells survive and which die.</strong></p>
<p>To compute this, we need two things:</p>
<ul>
<li>who are the <strong>neighbors</strong>?</li>
<li>what are their <strong>statuses</strong>?</li>
<li>what are the rules for a cell to survive, die or revive depending on its neighbors?</li>
</ul>
<p>First, the neighbors:</p>
<p>Imagine a cell at the center...</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9mhheisl0qgwxzmanamq.png" alt="neighboring cells"></p>
<p>The neighboring cells are:</p>
<ul>
<li>top-left == rowId-1, columnId-1</li>
<li>top == rowId-1, columnId</li>
<li>top-right == rowId-1, columnId+1</li>
<li>left = columnId-1</li>
<li>right = columnId+1</li>
<li>bottom-left == rowId+1, columnId-1</li>
<li>bottom == rowId+1, columnId</li>
<li>bottom-right == rowId+1, columnId+1</li>
</ul>
<p>Sometimes, some of these neighbors may not exist. Example: top-left-most cell (ie, start of the grid) does not have a top-* or left neighbor.</p>
<p>But that's okay.</p>
<p><strong>We know the formula for neighbors now. We can use this to get a list of &quot;valid&quot; neighboring cells.</strong></p>
<pre><code class="language-elm">isTopLeft : Position -&gt; Cell -&gt; Bool
isTopLeft ( rowId, columnId ) { position } =
    rowId == Tuple.first position - 1 &amp;&amp; columnId == Tuple.second position - 1


isTop : Position -&gt; Cell -&gt; Bool
isTop ( rowId, columnId ) { position } =
    rowId == Tuple.first position - 1 &amp;&amp; columnId == Tuple.second position


isTopRight : Position -&gt; Cell -&gt; Bool
isTopRight ( rowId, columnId ) { position } =
    rowId == Tuple.first position - 1 &amp;&amp; columnId == Tuple.second position + 1


isLeft : Position -&gt; Cell -&gt; Bool
isLeft ( rowId, columnId ) { position } =
    Tuple.first position == rowId &amp;&amp; columnId == Tuple.second position - 1


isRight : Position -&gt; Cell -&gt; Bool
isRight ( rowId, columnId ) { position } =
    Tuple.first position == rowId &amp;&amp; columnId == Tuple.second position + 1


isBottomLeft : Position -&gt; Cell -&gt; Bool
isBottomLeft ( rowId, columnId ) { position } =
    rowId == Tuple.first position + 1 &amp;&amp; columnId == Tuple.second position - 1


isBottom : Position -&gt; Cell -&gt; Bool
isBottom ( rowId, columnId ) { position } =
    rowId == Tuple.first position + 1 &amp;&amp; columnId == Tuple.second position


isBottomRight : Position -&gt; Cell -&gt; Bool
isBottomRight ( rowId, columnId ) { position } =
    rowId == Tuple.first position + 1 &amp;&amp; columnId == Tuple.second position + 1


getNeighboringCells : Cell -&gt; Grid -&gt; Array Cell
getNeighboringCells cell grid =
    Array.filter
        (\cell_ -&gt;
            isTopLeft cell_.position cell
                || isTopRight cell_.position cell
                || isTop cell_.position cell
                || isLeft cell_.position cell
                || isRight cell_.position cell
                || isBottomLeft cell_.position cell
                || isBottomRight cell_.position cell
                || isBottom cell_.position cell
        )
        grid
</code></pre>
<p>We can test this in a Debug statement:</p>
<pre><code class="language-bash">&gt; getNeighboringCells { status = Alive, position = ( 1, 1 ) } grid

Array.fromList [{ position = (1,2), status = Dead },{ position = (2,1), status = Dead },{ position = (2,2), status = Dead }]
</code></pre>
<p>Now that we have the neighbors (and their status), we can compute if the cell will be alive, dead or revived from death.</p>
<p>These are the rules:</p>
<ul>
<li>a &quot;live&quot; cell will live in the next generation if it has exactly 2 or 3 &quot;live&quot; neighbors</li>
<li>a &quot;live&quot; cell will die if it has less than 2 or more than 3 &quot;live&quot; neighbors</li>
<li>a &quot;dead&quot; cell will come alive if it has exactly 3 &quot;live&quot; neighbors</li>
<li>a &quot;dead&quot; cell will remain dead if it has less than 3 neighbors.</li>
</ul>
<pre><code class="language-elm">newStatusOfCell : Cell -&gt; Grid -&gt; Cell
newStatusOfCell cell grid =
    let
        neighboringCells =
            getNeighboringCells cell grid

        totalCellsAlive =
            Array.filter (\{ status } -&gt; status == Alive) neighboringCells |&gt; Array.length
    in
    case cell.status of
        Alive -&gt;
            if totalCellsAlive == 2 || totalCellsAlive == 3 then
                { cell | status = Alive }

            else
                { cell | status = Dead }

        Dead -&gt;
            if totalCellsAlive == 3 then
                { cell | status = Alive }

            else
                cell
</code></pre>
<p>It's worth adding a &quot;Next&quot; Msg to our app so it's easy to test the above function right away.</p>
<pre><code class="language-elm">type Msg
    = UpdateGrid Grid
    | Next -- the new Msg

update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )
update msg model =
    case msg of
        UpdateGrid grid -&gt;
            ( { model | grid = grid }, Cmd.none )

        Next -&gt;
            ( { model | grid = Array.map (\cell -&gt; newStatusOfCell cell model.grid) model.grid }, Cmd.none )

view : Model -&gt; Html Msg
view model =
    div []
        [viewGrid model, div [] [ button [ onClick Next ] [text &quot;Next Gen&quot;] ] ]

</code></pre>
<p>This renders a &quot;Next Gen&quot; button under the grid and clicking that advances the grid to the next generation. <a href="https://ellie-app.com/nwBTFj64qZ5a1">You can fiddle around with the app at this stage here</a></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m962dw4hoymvgs5o3ptl.png" alt="grid with next button"></p>
<h2>3. Making the grid come alive</h2>
<p>The final step is to make the grid come alive. <strong>That is, it should step to the next generation automatically!</strong></p>
<p>To do this, we'll add a <code>Tick</code> Msg that calls itself. Since we have the <code>Next</code> msg that computes the next generation of the board, we can re-use that!</p>
<pre><code class="language-elm">type Msg
    = UpdateGrid Grid
    | Next
    | Tick -- we added a new Msg type


update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )
update msg model =
    case msg of
        UpdateGrid grid -&gt;
            -- existing code as is

        Next -&gt;
            -- existing code as is

        Tick -&gt;
            let
                ( newModel, _ ) =
                    update Next model -- grab the new model/grid
            in
            ( newModel
            , Task.perform (\_ -&gt; Tick) (Process.sleep 1000.0)
            )
</code></pre>
<p>I'm using <code>Process.sleep</code> to mimic the behavior of Javascript's <code>setTimeout</code>. And then I use the <code>Task.perform</code> to <em>perform</em> some task – in this case, <code>(\_ -&gt; Tick)</code>.</p>
<p>To trigger this, we'll add a <code>Start</code> button:</p>
<pre><code class="language-elm">view : Model -&gt; Html Msg
view model =
    div []
        [ viewGrid model
        , div []
            [ button [ onClick Next ] [ text &quot;Next Gen&quot; ]
            , button [ onClick Tick ] [ text &quot;Start&quot; ]
            ]
        ]
</code></pre>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ynmlwn45cptad7xafi28.png" alt="final render"></p>
<p>If you now click on the <code>Start</code> button, the grid starts changing every second, essentially moving to the next generation.</p>
<p>And the cells live/die or come alive depending on the rules.</p>
<p><a href="https://ellie-app.com/nwCyhPmDfFja1">Here's the final output you can play with</a>.</p>
<p>You could try and add more features:</p>
<ul>
<li>a &quot;Pause&quot; function.</li>
<li>a way to stop the game when either every cell dies or when it reaches an equilibrium.</li>
<li>configurable size of the grid.</li>
</ul>
]]></description>
<pubDate>Tue, 01 Aug 2023 12:00:00 +0530</pubDate>
</item><item>
<title>How to render a basic calendar UI in Elm</title>
<link>https://code.druchan.com/elm-calendar</link>
<guid>https://code.druchan.com/elm-calendar</guid>
<description><![CDATA[<p>The beauty of a language like <a href="https://elm-lang.org">Elm</a> (and other lambda-calculus / functional programming inspired languages) is that <strong>there's very little transformation involved in going from an idea to code. And that seems to have a big impact on getting things done.</strong></p>
<p>Making a basic calendar UI turned out to be a great example of this.</p>
<p>Here's the final output I aimed for:</p>
<p><img src="https://github.com/chandru89new/elm-simple-calendar/blob/main/screens/final_out_example.png?raw=true" alt="final output example"></p>
<p>I started by thinking about the lowest unit: the month.</p>
<p><strong>Given a month (and a year), can I get this?</strong></p>
<p><img src="https://github.com/chandru89new/elm-simple-calendar/blob/main/screens/month_render.png?raw=true" alt="month render"></p>
<p>My first idea was to do this:</p>
<ul>
<li>get all dates in a given month-year.</li>
<li>get some padding for the first week and padding for the last week so that I can fill them with empty blocks (this depends on when the week starts)</li>
<li>pass this data to a rendering function!</li>
</ul>
<p>The type of data we choose should be good enough to make it possible to render it easily.</p>
<p>So, in our case, we're rendering dates. Lists of dates.</p>
<p>And because we're rendering &quot;rows&quot; of dates, each row is a week of dates.</p>
<p>So the data structure I'm going for is this:</p>
<pre><code class="language-elm">-- assuming `Date` is some valid date representation
type Week = List Date
type MonthData = List Week
</code></pre>
<p><strong><code>MonthData</code> could have 5-6 items, each item being a <code>Week</code>. And each <code>Week</code> being a list of 7 <code>Date</code>s.</strong></p>
<p>I took a look at Elm's <a href="https://package.elm-lang.org/packages/elm/time/latest/">time</a> library to see if that fit the bill. Turns out it didnt. It's too low-level and involves a lot of <code>Task</code> mechanics that was an overkill.</p>
<p>Looking around, <strong>I found Justin's <a href="https://package.elm-lang.org/packages/justinmimbs/date/latest/">date</a> library which seemed like a great candidate.</strong></p>
<p>(Edit: In fact, it turned out to be a life-saver. It has everything we need.)</p>
<p>Justin's <a href="https://package.elm-lang.org/packages/justinmimbs/date/latest/">date</a> library had these two functions which were interesting:</p>
<pre><code class="language-elm">ceiling : Interval -&gt; Date -&gt; Date
-- Round up a date to the beginning of the closest interval. The resulting date will be greater than or equal to the one provided.

floor : Interval -&gt; Date -&gt; Date
-- Round down a date to the beginning of the closest interval. The resulting date will be less than or equal to the one provided.
</code></pre>
<p>So, if I wanted to find the nearest &quot;previous&quot; Sunday before 1st July 2023, I can do this:</p>
<pre><code class="language-elm">import Date
import Time

result = Date.floor Date.Sunday (Date.fromCalendarDate 2023 Time.Jul 1)
</code></pre>
<p>Testing this in REPL:</p>
<pre><code class="language-bash">&gt; result |&gt; Date.format &quot;EEE, d MM y&quot;
&quot;Sun, 25 Jun 2023&quot; : String
</code></pre>
<p>And if I wanted to nearest &quot;next&quot; Saturday after 31st of July 2023, I can do this:</p>
<pre><code class="language-elm">import Date
import Time

result = Date.ceiling Date.Saturday (Date.fromCalendarDate 2023 Time.Jul 31)
</code></pre>
<p>In REPL:</p>
<pre><code class="language-bash">&gt; result |&gt; format &quot;EEE, d MMM y&quot;
&quot;Sat, 5 Aug 2023&quot; : String
</code></pre>
<p>That's fantastic. <strong>Now, my logic is simplified to this:</strong></p>
<ul>
<li>take start of week (eg <code>Sunday</code>), month and year as inputs</li>
<li>compute the &quot;proper&quot; start date (which is the nearest <code>start of week</code> for a given first-day of the month)</li>
<li>compute the &quot;proper&quot; end date (which is the nearest <code>start of week minus one</code> for a given last-day of the month)</li>
<li>get all dates falling between these two dates (including both) – this becomes a list of all dates to render</li>
<li>split them into groups of 7 and we have a list of weeks... which is the same as our <code>MonthData</code>!</li>
</ul>
<h2>Getting the start date for a given month, year and start of week:</h2>
<p>First step: take month, year and start of week and output the right/proper start date.</p>
<p>Example what we want:</p>
<pre><code>-- `getProperStartDate : StartOfWeek -&gt; Month -&gt; Year -&gt; Date`
getProperStartDate Sunday July 2023 == &quot;25th June 2023&quot;
getProperStartDate Sunday June 2023 == &quot;28th May 2023&quot;

-- ignore the fact that the result is string. that's just for demonstration
</code></pre>
<p>To get here, we just have to use the <code>floor</code> function from the <code>Date</code> library:</p>
<pre><code class="language-elm">import Date
import Time

getProperStartDate : StartOfWeek -&gt; Month -&gt; Year -&gt; Date.Date
getProperStartDate startOfWeek month year =
    Date.floor (weekdayToInterval startOfWeek) (Date.fromCalendarDate year month 1)

-- we also need a function that converts a Time.Weekday to a Date.Interval
-- to use in the `getProperStartDate` function
weekdayToInterval : Time.Weekday -&gt; Date.Interval
weekdayToInterval weekday =
    case weekday of
        Time.Sun -&gt;
            Date.Sunday

        Time.Mon -&gt;
            Date.Monday

        Time.Tue -&gt;
            Date.Tuesday

        Time.Wed -&gt;
            Date.Wednesday

        Time.Thu -&gt;
            Date.Thursday

        Time.Fri -&gt;
            Date.Friday

        Time.Sat -&gt;
            Date.Saturday
</code></pre>
<p>Test in REPL:</p>
<pre><code class="language-bash">&gt; getProperStartDate Time.Sun Time.Jul 2023 |&gt; Date.format &quot;EEE, d MMM y&quot;
&quot;Sun, 25 Jun 2023&quot; : String
</code></pre>
<p>Next up, <strong>let's also write a function to get the proper end date for a given month, year and start of week.</strong></p>
<p>This time, it's not as straight-forward.</p>
<p>Take 31st July 2023 and Sunday (for start of week) as an example:</p>
<ul>
<li>31st July 2023 is a Monday</li>
<li>The next closest Sunday is 6th August 2023.</li>
<li>But we don't need a &quot;Sunday&quot;. We need the next closest &quot;Saturday&quot;.</li>
<li>At first, I thought &quot;hey we could compute the actual end of week day from the given start-of-week day&quot; but that's a lot of code. Instead, we can just get the next-closest Sunday and then reduce 1 day!</li>
</ul>
<p>And here's that logic:</p>
<pre><code class="language-elm">getProperEndDate : StartOfWeek -&gt; Month -&gt; Year -&gt; Date.Date
getProperEndDate startOfWeek month year =
    let
        endDate =
            Date.add Date.Months 1 (Date.fromCalendarDate year month 1) |&gt; Date.add Date.Days -1

        endDateIsStartOfWeek =
            Date.weekday endDate == startOfWeek
    in
    if endDateIsStartOfWeek then
        Date.add Date.Days 7 endDate

    else
        Date.ceiling (weekdayToInterval startOfWeek) endDate |&gt; Date.add Date.Days -1
</code></pre>
<ul>
<li>First we calculate the end date of the month.</li>
<li>Then we find the actual end date we need – this happens to be the closest &quot;start of week&quot; day minus 1 (so that it's the end of the week).</li>
<li>One small additional condition there that checks if the actual end date of the month also happens to be the start of the week. In that case, we add 7 days to get to the closest end of week day.</li>
</ul>
<p>Testing this in REPL:</p>
<pre><code class="language-bash">&gt; getProperEndDate Time.Sun Time.Jul 2023 |&gt; format &quot;EEE, d MMM y&quot;
&quot;Sat, 5 Aug 2023&quot; : String
</code></pre>
<p>Now that we know the proper start and end dates, we can use them to get all dates in that range.</p>
<pre><code class="language-elm">getDatesBetween : Date.Date -&gt; Date.Date -&gt; List Date.Date
getDatesBetween start end =
    Date.range Date.Day 1 start (Date.add Date.Days 1 end)
</code></pre>
<p>This <code>Date.range</code> function excludes the last date in the range. But we need the proper end date as well, so we just add one.</p>
<p>And now that we have all the dates to render, we can group them to get a list of weeks!</p>
<p>To do this, I'm using the <a href="https://package.elm-lang.org/packages/elm-community/list-extra/latest/"><code>List.Extra</code></a> library's <code>groupsOf</code> function:</p>
<pre><code class="language-elm">getMonth : List Date.Date -&gt; List Week
getMonth =
    List.Extra.groupsOf 7
</code></pre>
<p>And of course, we need to take just month, year and start of week as inputs and get back an entire month of dates:</p>
<pre><code class="language-elm">getDatesForMonth : Month -&gt; Year -&gt; List Week
getDatesForMonth month year =
    let
        start =
            getProperStartDate Time.Sun month year

        end =
            getProperEndDate Time.Sun month year

        dates =
            getDatesBetween start end
    in
    getMonth dates
</code></pre>
<p>Yes, we're just hard-coding the start of the week (<code>Time.Sun</code>) for now. We can switch this later to be something that the function accepts as an input.</p>
<p>Testing these in REPL:</p>
<pre><code class="language-bash">&gt; getDatesForMonth Time.Jul 2023
[[RD 738696,RD 738697,RD 738698,RD 738699,RD 738700,RD 738701,RD 738702],[RD 738703,RD 738704,RD 738705,RD 738706,RD 738707,RD 738708,RD 738709],[RD 738710,RD 738711,RD 738712,RD 738713,RD 738714,RD 738715,RD 738716],[RD 738717,RD 738718,RD 738719,RD 738720,RD 738721,RD 738722,RD 738723],[RD 738724,RD 738725,RD 738726,RD 738727,RD 738728,RD 738729,RD 738730],[RD 738731,RD 738732,RD 738733,RD 738734,RD 738735,RD 738736,RD 738737]]
</code></pre>
<p>The <code>RD Int</code> is a native representation of the <code>Date</code> library.</p>
<p>We can format this to be human-friendly and check that the results are OK:</p>
<pre><code class="language-bash">&gt; getDatesForMonth Time.Jul 2023 |&gt; List.map (List.map (Date.format &quot;EEE, d MMM y&quot;))
[[&quot;Sun, 25 Jun 2023&quot;,&quot;Mon, 26 Jun 2023&quot;,&quot;Tue, 27 Jun 2023&quot;,&quot;Wed, 28 Jun 2023&quot;,&quot;Thu, 29 Jun 2023&quot;,&quot;Fri, 30 Jun 2023&quot;,&quot;Sat, 1 Jul 2023&quot;],[&quot;Sun, 2 Jul 2023&quot;,&quot;Mon, 3 Jul 2023&quot;,&quot;Tue, 4 Jul 2023&quot;,&quot;Wed, 5 Jul 2023&quot;,&quot;Thu, 6 Jul 2023&quot;,&quot;Fri, 7 Jul 2023&quot;,&quot;Sat, 8 Jul 2023&quot;],[&quot;Sun, 9 Jul 2023&quot;,&quot;Mon, 10 Jul 2023&quot;,&quot;Tue, 11 Jul 2023&quot;,&quot;Wed, 12 Jul 2023&quot;,&quot;Thu, 13 Jul 2023&quot;,&quot;Fri, 14 Jul 2023&quot;,&quot;Sat, 15 Jul 2023&quot;],[&quot;Sun, 16 Jul 2023&quot;,&quot;Mon, 17 Jul 2023&quot;,&quot;Tue, 18 Jul 2023&quot;,&quot;Wed, 19 Jul 2023&quot;,&quot;Thu, 20 Jul 2023&quot;,&quot;Fri, 21 Jul 2023&quot;,&quot;Sat, 22 Jul 2023&quot;],[&quot;Sun, 23 Jul 2023&quot;,&quot;Mon, 24 Jul 2023&quot;,&quot;Tue, 25 Jul 2023&quot;,&quot;Wed, 26 Jul 2023&quot;,&quot;Thu, 27 Jul 2023&quot;,&quot;Fri, 28 Jul 2023&quot;,&quot;Sat, 29 Jul 2023&quot;],[&quot;Sun, 30 Jul 2023&quot;,&quot;Mon, 31 Jul 2023&quot;,&quot;Tue, 1 Aug 2023&quot;,&quot;Wed, 2 Aug 2023&quot;,&quot;Thu, 3 Aug 2023&quot;,&quot;Fri, 4 Aug 2023&quot;,&quot;Sat, 5 Aug 2023&quot;]]
</code></pre>
<p>Now that I have this data structure, all I need to do is render it as a month!</p>
<p>We can work this inside-out. That is, we can build functions to render a date, a week and then combine these to render the month.</p>
<p>Here's a function to render the date:</p>
<p>(I'm using Tailwind classes to simplify styling)</p>
<pre><code class="language-elm">viewDate : Date.Date -&gt; Html Msg
viewDate date =
    H.div [ Attr.class &quot;flex items-center justify-center&quot; ] [ H.text &lt;| Date.format &quot;d&quot; date ]
</code></pre>
<p>And the week:</p>
<pre><code class="language-elm">viewWeek : Week -&gt; Html Msg
viewWeek dates =
    H.div [ Attr.class &quot;grid grid-cols-7 items-center gap-4&quot; ] (List.map viewDate dates)
</code></pre>
<p>I'm using CSS <code>grid</code> to make it easy to arrange the dates. Each date is <code>flex</code> and center-aligned (see the <code>viewDate</code> function above). And the week-render takes care of rendering all dates in a 7-column grid.</p>
<p>And the view month is just this:</p>
<pre><code class="language-elm">viewMonth : List Week -&gt; Html Msg
viewMonth weeks =
    H.div [] (List.map viewWeek weeks)
</code></pre>
<p>And of course, we need to render the week header as well which lists the weekdays.</p>
<p>To do this, I'm going to be a bit hacky:</p>
<ul>
<li>We already have a list of weeks in <code>List Week</code>.</li>
<li>We can take the &quot;first&quot; element of this list and</li>
<li>format each date in the list to just extract the weekday</li>
<li>and use the resulting list to render the week header!</li>
</ul>
<p>Expressed in code, we start with the week header view function which takes a list of weeks and renders a list of weekday headers.</p>
<pre><code class="language-elm">viewWeekHeader : Week -&gt; Html Msg
viewWeekHeader week =
    H.div [ Attr.class &quot;grid grid-cols-7 items-center gap-2&quot; ] &lt;|
        List.map (\date -&gt; H.div [ Attr.class &quot;flex items-center justify-center&quot; ] [ H.text &lt;| Date.format &quot;EEEEE&quot; date ]) week
</code></pre>
<p>Combining all of this into a view function:</p>
<pre><code class="language-elm">view : Model -&gt; Html Msg
view _ =
    let
        dates =
            getDatesForMonth Time.Jul 2023
    in
    H.div [ Attr.class &quot;w-72&quot; ]
        [ viewWeekHeader (Maybe.withDefault [] (List.head dates))
        , viewMonth dates
        ]
</code></pre>
<p>Here's what it renders as:</p>
<p><img src="https://github.com/chandru89new/elm-simple-calendar/blob/main/screens/month_render_initial.png?raw=true" alt="month render ugly"></p>
<p><strong>It's ugly, shows dates from the previous/next months and there's so much room for improvement.</strong></p>
<p>But we've got the basics right and that's good enough to boot.</p>
<p>The first order of business now is to <strong>not show dates which are not part of the month.</strong></p>
<p>In the example above, that's 25th - 30th (June) and 1st - 5th (August).</p>
<p>What we have is a long list of dates. We need to somehow <em>know</em> if a date in the list is part of the current month (eg July) or not.</p>
<p>Let's think in terms of the type:</p>
<pre><code class="language-elm">type alias CalendarDate =
    { date : Date.Date, dateInCurrentMonth : Bool }
</code></pre>
<p>And we'll change our week to be:</p>
<pre><code class="language-elm">type alias Week =
    List CalendarDate
</code></pre>
<p><strong>The moment we make this change, the Elm compiler will start guiding us through the functions that need updating.</strong></p>
<p>We'll change the <code>getMonth</code> one first:</p>
<pre><code class="language-elm">getMonth : List CalendarDate -&gt; List Week
getMonth =
    List.Extra.groupsOf 7
</code></pre>
<p>And:</p>
<pre><code class="language-elm">getDatesForMonth : Month -&gt; Year -&gt; List Week
getDatesForMonth month year =
    let
        start =
            getProperStartDate Time.Sun month year

        end =
            getProperEndDate Time.Sun month year

        dates =
            getDatesBetween start end
                |&gt; List.map (\date -&gt; { date = date, dateInCurrentMonth = Date.month date == month })
    in
    getMonth dates
</code></pre>
<p>And the last things we need to fix are the <code>viewDate</code> and <code>viewWeekHeader</code> functions. The logic is simple: if the date is not that of current month, we'll set the opacity to 0.</p>
<pre><code class="language-elm">viewDate : CalendarDate -&gt; Html Msg
viewDate { date, dateInCurrentMonth } =
    H.div
        [ Attr.class &quot;flex items-center justify-center&quot;
        , Attr.class
            (if dateInCurrentMonth then
                &quot;&quot;

             else
                &quot;opacity-0&quot;
            )
        ]
        [ H.text &lt;| Date.format &quot;d&quot; date ]

viewWeekHeader : Week -&gt; Html Msg
viewWeekHeader week =
    H.div [ Attr.class &quot;grid grid-cols-7 items-center gap-2&quot; ] &lt;|
        List.map (\{ date } -&gt; H.div [ Attr.class &quot;flex items-center justify-center&quot; ] [ H.text &lt;| Date.format &quot;EEEEE&quot; date ]) week
</code></pre>
<p>This gets us to here:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9terox36aq3afqb2j0ct.png" alt="refining month render"></p>
<p><strong>One of the things that's been bothering me about this render is that there's a lot of duplication because of the way we're rendering the rows.</strong></p>
<p>Each row is it's own &quot;grid&quot;, instead of the whole month being a grid. (And the week header is also it's own &quot;grid&quot;).</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o0dn9mcoordfj14sgki9.png" alt="too many divs"></p>
<p>We can fix this.</p>
<p>We want a structure like this:</p>
<pre><code>&lt;div class=&quot;grid grid-cols-7 items-center gap-2&quot;&gt;
	&lt;div&gt;S&lt;/div&gt;
	&lt;div&gt;M&lt;/div&gt;
	&lt;div&gt;T&lt;/div&gt;
	... all weekday headers
	...
	&lt;div&gt;1&lt;/div&gt;
	&lt;div&gt;2&lt;/div&gt;
	... all dates
&lt;/div&gt;
</code></pre>
<p>We can do this in two steps:</p>
<ol>
<li>first, we'll write one generic container <code>viewBox</code> which wraps all its children in the grid</li>
<li>then, we'll ensure all our viewMonth/viewWeek functions return a list of <code>div</code>s that we can just render inside the <code>viewBox</code></li>
</ol>
<pre><code class="language-elm">viewBox : List (Html Msg) -&gt; Html Msg
viewBox =
    H.div [ Attr.class &quot;grid grid-cols-7 gap-2 items-center&quot; ]
</code></pre>
<p>And to make life easier, I'll also add a <code>viewItem</code> (which is basically the date render):</p>
<pre><code class="language-elm">viewItem : List (Html Msg) -&gt; Html Msg
viewItem =
    H.div [ Attr.class &quot;flex items-center justify-center&quot; ]
</code></pre>
<p>And now, we'll change the other view functions so they return a <code>List (Html Msg)</code> instead of <code>Html Msg</code>:</p>
<pre><code class="language-elm">viewWeek : Week -&gt; List (Html Msg)
viewWeek dates =
    List.map viewDate dates


viewMonth : List Week -&gt; List (Html Msg)
viewMonth weeks =
    List.concatMap viewWeek weeks


viewWeekHeader : Week -&gt; List (Html Msg)
viewWeekHeader week =
    List.map (\{ date } -&gt; H.div [ Attr.class &quot;flex items-center justify-center&quot; ] [ H.text &lt;| Date.format &quot;EEEEE&quot; date ]) week
</code></pre>
<p>In the <code>viewMonth</code> function, we use the <code>List.concatMap</code> function because:</p>
<ul>
<li>viewMonth is a list.map over weeks using the <code>viewWeek</code> function</li>
<li>the <code>viewWeek</code> function returns a list</li>
<li>so the final result is list of lists</li>
<li>which we <code>concat</code> to flatten into a list.</li>
</ul>
<p>Finally, we'll modify the main view function:</p>
<pre><code class="language-elm">view : Model -&gt; Html Msg
view _ =
    let
        year =
            2023

        month =
            Time.Jul

        dates =
            getDatesForMonth month year
    in
    H.div [ Attr.class &quot;w-72&quot; ]
        [ H.div [ Attr.class &quot;p-2&quot; ] [ H.text (Date.format &quot;MMMM YYYY&quot; (Date.fromCalendarDate year month 1)) ]
        , viewBox &lt;| List.concat [ viewWeekHeader (Maybe.withDefault [] (List.head dates)), viewMonth dates ]
        ]
</code></pre>
<p>The main change is that we're now using the <code>viewBox</code> function (so we modify the input to it).</p>
<p>And the other thing is we added this bit:</p>
<pre><code class="language-elm">H.div [ Attr.class &quot;p-2&quot; ] [ H.text (Date.format &quot;MMMM YYYY&quot; (Date.fromCalendarDate year month 1))
</code></pre>
<p>which adds a month-year header.</p>
<p>Our final result:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iudzghx0x6ega1qhow9n.png" alt="month final"></p>
<p>All that remains is to repeat this for each month in a given year.</p>
<p>To do this, we can just write out all the months of a year, and loop over them:</p>
<pre><code class="language-elm">view : Model -&gt; Html Msg
view _ =
    let
        year =
            2023

        months =
            [ Time.Jan
            , Time.Feb
            , Time.Mar
            , Time.Apr
            , Time.May
            , Time.Jun
            , Time.Jul
            , Time.Aug
            , Time.Sep
            , Time.Oct
            , Time.Nov
            , Time.Dec
            ]
    in
    H.div [ Attr.class &quot;p-8 grid grid-cols-4 gap-4 items-stretch&quot; ]
        (List.map (\month -&gt; viewMonthBox month year) months)
</code></pre>
<p>This produces:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/du72s76y34mnkyewcex1.png" alt="full year but with bug"></p>
<p>Oh no! Here's a problem: the first month reads <code>January 2022</code>.</p>
<p>Turns out, the formatting string I was using is wrong:</p>
<p>Instead of <code>MMMM YYYY</code>, I need to be using <code>MMMM y</code>. (<a href="http://www.unicode.org/reports/tr35/tr35-43/tr35-dates.html#Date_Format_Patterns">More info about these format strings here</a>.)</p>
<pre><code class="language-elm">[ H.div [ Attr.class &quot;p-2 text-center&quot; ] [ H.text (Date.format &quot;MMMM y&quot; (Date.fromCalendarDate year month 1)) ]
</code></pre>
<p>And that fixes the problem:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cmitpkrnmhakbvag0pbg.png" alt="full year final"></p>
<p>The <a href="https://github.com/chandru89new/elm-simple-calendar">full source code can be found here</a>.</p>
]]></description>
<pubDate>Mon, 31 Jul 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Defining a valid trade using typeclass</title>
<link>https://code.druchan.com/max-stock-profit-leetcode-typeclass</link>
<guid>https://code.druchan.com/max-stock-profit-leetcode-typeclass</guid>
<description><![CDATA[<p><a href="/max-stock-profit-leetcode">In the last note posted</a>, we &quot;implicitly&quot; defined some valid trades.</p>
<p>Two data structures specifically had this notion associated with them:</p>
<pre><code class="language-haskell">-- type Day = Int
-- type Price = Int

type StockDay = Tuple Price Day
-- and
data BuySell = BuySell StockDay StockDay
</code></pre>
<p>Two <code>StockDay</code>s could be valid only if they happen on subsequent days.</p>
<p>That is:</p>
<pre><code class="language-haskell">a = Tuple _ 1
b = Tuple _ 6

c = Tuple _ 8
d = Tuple _ 4
</code></pre>
<p>In this, <code>a</code> followed by <code>b</code> can be a valid trade because the &quot;day&quot; order makes sense. (That is <code>a</code> is on 1st day and <code>b</code> is on 6th day - buy on 1st and sell on 6th).</p>
<p>But <code>c</code> followed by <code>d</code> (and <code>b</code> followed by <code>d</code>) are not valid because the day orders are reversed.</p>
<p>This notion comes into the picture when we &quot;pair&quot; these to make a <code>BuySell</code> combination.</p>
<p>We expressed this as a <code>Maybe</code>:</p>
<pre><code class="language-haskell">makeBuySell :: StockDay -&gt; StockDay -&gt; Maybe BuySell
makeBuySell t1@(Tuple p1 a) t2@(Tuple p2 b) =
  if a &lt; b &amp;&amp; p1 &lt; p2 then Just (BuySell t1 t2) else Nothing
</code></pre>
<p>That <code>a &lt; b</code> part takes care of the &quot;valid trade&quot; logic.</p>
<p>It's OK and there's no need to &quot;improve&quot; this...</p>
<p>But now, there's also this other function that we use in our computation:</p>
<pre><code class="language-haskell">isValidTradeDayOrder :: BuySell -&gt; BuySell -&gt; Boolean
isValidTradeDayOrder (BuySell (Tuple _ a) (Tuple _ b)) (BuySell (Tuple _ c) (Tuple _ d)) =
  (a &lt; b &amp;&amp; c &lt; d &amp;&amp; c &gt; b) || (c &lt; d &amp;&amp; a &lt; b &amp;&amp; d &lt; a)
</code></pre>
<p>Here, we're trying to confirm if two buy-sell pairs are actually valid by making sure the dates/days align. (Remember that <em>within</em> a <code>BuySell</code> pair, the <code>StockDay</code>s are valid thanks to our <code>makeBuySell</code> function. Essentially, a <code>BuySell</code> represents valid, profitable buy-and-sell operation).</p>
<p>Again, though, in the case of a <code>isValidTradeDayOrder</code>, we're dealing with this idea of &quot;valid trade&quot;.</p>
<p>I decided (or rather, wanted to experiment) if this general idea of a &quot;valid trade&quot; can be encoded into the code as a general function.</p>
<p>Purescript, like Haskell, allows you to define your own <a href="https://book.purescript.org/chapter6.html">typeclasses</a> and then define instances for your data types.</p>
<p>So, here's a generic <code>ValidTrade</code> class:</p>
<pre><code class="language-haskell">class ValidTrade a where
  validTrade :: a -&gt; a -&gt; Boolean
</code></pre>
<p>Any datatype using the <code>ValidTrade</code> class must simply describe a <code>validTrade</code> function.</p>
<p>And so, here are the definitions for both the <code>StockDay</code> and <code>BuySell</code> data types:</p>
<pre><code class="language-haskell">instance ValidTrade BuySell where
  validTrade (BuySell (Tuple _ a) (Tuple _ b)) (BuySell (Tuple _ c) (Tuple _ d)) =
    (a &lt; b &amp;&amp; c &lt; d &amp;&amp; c &gt; b) || (c &lt; d &amp;&amp; a &lt; b &amp;&amp; d &lt; a)

instance ValidTrade StockDay where
  validTrade (Tuple _ d1) (Tuple _ d2) = d1 &lt; d2
</code></pre>
<p>I also added an <code>infix</code> to help make the code a little succinct:</p>
<pre><code class="language-haskell">infix 1 validTrade as ??
</code></pre>
<p>Now, I could get rid of the <code>isValidTradeDayOrder</code> function in totality and replace some instances with <code>??</code>:</p>
<pre><code class="language-haskell">makeBuySell :: StockDay -&gt; StockDay -&gt; Maybe BuySell
makeBuySell t1@(Tuple p1 _) t2@(Tuple p2 _) =
  if (t1 ?? t2) &amp;&amp; p1 &lt; p2 then Just (BuySell t1 t2) else Nothing

bestBuySellPair :: BuySell -&gt; SortedArray (BuySell) -&gt; Int -&gt; Maybe BestCandidate -&gt; Maybe BestCandidate
bestBuySellPair buySell (SortedArray []) maxProfitSoFar bestTradeSoFar =
  if buySellProfit buySell &gt; maxProfitSoFar then (Just $ Tuple buySell Nothing)
  else bestTradeSoFar
bestBuySellPair buySell (SortedArray tradePairs) maxProfitSoFar bestTradeSoFar =
  case head tradePairs of
    Just h -&gt;
      -- notice the ?? here. we've replaced the `isValidTradeDayOrder` function with ??
      if ((buySell ?? h) &amp;&amp; (buySellProfit buySell + buySellProfit h) &gt; maxProfitSoFar) then bestBuySellPair buySell (fromMaybe (SortedArray []) (map SortedArray $ tail tradePairs)) (buySellProfit buySell + buySellProfit h) (Just $ Tuple buySell (Just h))
      else bestBuySellPair buySell (fromMaybe (SortedArray []) (map SortedArray $ tail tradePairs)) maxProfitSoFar bestTradeSoFar
    Nothing -&gt;
      if buySellProfit buySell &gt; maxProfitSoFar then (Just $ Tuple buySell Nothing)
      else bestTradeSoFar
</code></pre>
<p>And the script continues to run just fine:</p>
<pre><code class="language-bash">&gt; totalProfits [3,1,0,0,5,4,1]
5

&gt; totalProfits [3,1,0,0,1]
1

&gt; totalProfits [3,1,0,0,1,2,3]
3
</code></pre>
<p><a href="https://github.com/chandru89new/leetcode-stuff/tree/main/src/StockProfits.purs">Updated source-code here</a>.</p>
]]></description>
<pubDate>Sat, 10 Jun 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Another Leetcode episode in Purescript</title>
<link>https://code.druchan.com/max-stock-profit-leetcode</link>
<guid>https://code.druchan.com/max-stock-profit-leetcode</guid>
<description><![CDATA[<p>Lately, I've been enjoying solving some Leetcode problems <a href="/text-justify">as</a> <a href="/text-justify-2">is</a> <a href="/text-justify-3">clear</a> <a href="/int-to-roman">from my ramblings here</a>.</p>
<p>Today, I decided to pick <a href="https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/">this one</a>.</p>
<p>You are given a list of integers representing a stock's price for consecutive days.</p>
<pre><code class="language-text">[3,1,0,0,5,4,1,3]
</code></pre>
<p>You can buy on one day and then sell on any future day. You cannot buy and sell on the same day, nor can you buy more than once before selling it. Also, you can only buy and sell atmost twice. (That is, two buy-sell trades).</p>
<p>Given those conditions, find out what's the maximum profit you can make if you picked the best 1 or at most 2 trades from the list.</p>
<p>So for the example list of numbers from above, here's one potential candidate of trades that can net you the maximum profit:</p>
<pre><code class="language-text">stock prices: [3,1,0,0,5,4,1,3]
max profit = 7
because:
best trade #1 = 0 (buy on day 3 or 4), 5 (sell on 5th day) =&gt; profit = 5
best trade #2 = 1 (buy on 7th day), 3 (sell on 9th day) =&gt; profit = 2
total profit = 7
</code></pre>
<hr>
<p>I thought that the first thing to do was to sort the array ascending so that it was eaiser to know which trades netted the maximum profits.</p>
<p>Example:</p>
<pre><code class="language-text">unsorted: [3,1,0,0,5,4,1,3]
sorted: [0,0,1,1,3,3,4,5]
</code></pre>
<p>But of course, we have to also &quot;carry&quot; the information of which number (price) belongs to which day (original index) because to be able to calculate the best trade, we need to have a valid buy-sell sequence. If I sort the list, I know <code>0</code> and <code>5</code> net a great profit but those could not even be a valid sale as <code>5</code> <em>could</em> be on day 1 and <code>0</code> could be on day 2 (i.e, you cant sell before you buy).</p>
<p>Something like this would be a better data to carry around:</p>
<pre><code class="language-text">unsorted: [3,1,0,0,5,4,1,3]
sorted but with date info:
  [(0,3), (0,4), (1,2), (1,7), (3,1), (3,8), (4,6), (5,5)]
</code></pre>
<p>In Purescript, that's a <code>Tuple</code>:</p>
<pre><code class="language-haskell">type Price = Int

type Day = Int

type StockDay = Tuple Price Day

arrayToStockDay :: Array Int -&gt; SortedArray StockDay
arrayToStockDay xs = sortStockDay $ go xs 1 []
  where
  go :: Array Int -&gt; Int -&gt; Array StockDay -&gt; Array StockDay
  go [] _ acc = acc
  go ys day acc =
    case head ys of
      Just stockPrice -&gt; go (fromMaybe [] $ tail ys) (day + 1) (snoc acc (Tuple stockPrice day))
      Nothing -&gt; acc
</code></pre>
<p>(I will cover the <code>SortedArray</code> bit shortly; for now, treat it just as a wrapper around an <code>Array</code>)</p>
<p>This data structure lets me do an important thing: find all possible valid trades (single buy-and-sell) and their profit.</p>
<p>Suppose I take the first item: <code>(0,3)</code></p>
<p>I can now iterate over the rest of the items and make up a list of valid buy-sell trades like this:</p>
<pre><code class="language-text">buy = (0,3)
valid sells =
  (0,4)
  (1,7)
  (3,8)
  (4,6)
  (5,5)
</code></pre>
<p><code>(1,2)</code> and <code>(3,1)</code> are not valid because their &quot;days&quot; are before the chosen <code>(0,4)</code>.</p>
<p>And now, I can do one more thing: get profits for each of the valid sells.</p>
<pre><code class="language-text">buy = (0,3)
valid sells with profit info =
  (0,4) =&gt; 0
  (1,7) =&gt; 1
  (3,8) =&gt; 3
  (4,6) =&gt; 4
  (5,5) =&gt; 5
</code></pre>
<p>Let me do this for the next item in the list <code>(0,4)</code></p>
<pre><code class="language-text">buy = (0,4)
valid sells with profit info =
  (1,7) =&gt; 1
  (3,8) =&gt; 3
  (4,6) =&gt; 4
  (5,5) =&gt; 5
</code></pre>
<p>And more:</p>
<pre><code class="language-text">buy = (1,2)
valid sells with profit info =
  (1,7) =&gt; 0
  (3,8) =&gt; 2
  (4,6) =&gt; 3
  (5,5) =&gt; 4

buy = (1,7)
valid sells with profit info =
  (3,8) =&gt; 2
</code></pre>
<p>and so on.</p>
<p>Notice that because I'm sorting the array by the stock price, I only have to pick the <em>subsequent</em> items in an array when comparing one item with the rest.</p>
<p>What I need now is to somehow represent all that data (from the previous step) so that I can then use it to find out the <em>best</em> two sequential trades that can give me maximum profits.</p>
<p>I could simply rely on another &quot;pair&quot; like so:</p>
<pre><code class="language-text">buy = (0,3)
valid sells with profit info =
  (0,4) =&gt; 0
  (1,7) =&gt; 1
  (3,8) =&gt; 3
  (4,6) =&gt; 4
  (5,5) =&gt; 5

can be represented as:

[
  ( (0,3) , (0,4) ),
  ( (0,3) , (1,7) ),
  ( (0,3) , (3,8) )
  ( (0,3) , (4,6) ),
  ... and so on
]
</code></pre>
<p>And then you just combine all of them to have one giant list of all valid profit-making sales. By the time I get to code, I'll have one more check to filter out those valid trades that result in <em>some</em> profit so the list is really small. (Our example only has 14 valid profit-making buy-sell combinations).</p>
<p>Translating this logic to code, I worked from the smallest step: if I compare two stock-price-day items, I should be able to tell if the pair is a valid buy-sell trade:</p>
<pre><code class="language-haskell">import Data.Tuple (Tuple(..))
import Data.Maybe (Maybe(..))

-- represeting a buy sell trade
data BuySell = BuySell StockDay StockDay

makeBuySell :: StockDay -&gt; StockDay -&gt; Maybe BuySell
makeBuySell t1@(Tuple p1 a) t2@(Tuple p2 b) =
  if a &lt; b &amp;&amp; p1 &lt; p2 then Just (BuySell t1 t2) else Nothing
</code></pre>
<p>I use the <code>Maybe</code> data type to indicate if a comparison of two stock-price-day items results in a valid pair (valid by both day sequence and guaranteed profit).</p>
<p>I can use this <code>makeBuySell</code> function to do the comparison I wrote about earlier:</p>
<pre><code class="language-haskell">import Data.Array (snoc, head, tail)
import Data.Maybe (Maybe(..), fromMaybe)

makeBuySellList :: StockDay -&gt; SortedArray StockDay -&gt; Array BuySell
makeBuySellList sdp (SortedArray xs) = go sdp xs []
  where
  go _ [] acc = acc
  go _sdp ls acc = case head ls of
    Just h -&gt; case makeBuySell _sdp h of
      Nothing -&gt; go _sdp (fromMaybe [] $ tail ls) acc
      Just tp -&gt; go _sdp (fromMaybe [] $ tail ls) (snoc acc tp)
    Nothing -&gt; acc
</code></pre>
<p>Okay, pause here. I'm using a new data type called <code>SortedArray</code>. It's really just a <code>newtype</code>.</p>
<p>But why?</p>
<p>An <code>Array</code> can be sorted or unsorted. There is no way for me to know, by looking at a type signature, if an array being used by a function is sorted or not. Also, sorting can mean different things when we are talking about a <code>Tuple</code> or complex data structures.</p>
<p>So, I decided to use a <code>newtype</code> called <code>SortedArray</code> to indicate any array that is sorted (in some way).</p>
<pre><code class="language-haskell">newtype SortedArray a = SortedArray (Array a)
</code></pre>
<p>Again, I am not interested in the sort order or the sort logic here. All I want is a distinction between any random array (that could or could not be sorted) and a sorted array which is sorted using <em>some</em> logic that I don't care about.</p>
<p>Also, to make development easy, I decided to write a <code>Show</code> instance for it:</p>
<pre><code class="language-haskell">instance showSortedArray :: Show a =&gt; Show (SortedArray a) where
  show (SortedArray a) = &quot;SortedArray &quot; &lt;&gt; show a
</code></pre>
<p>I tried to derive a <code>Show</code> instance for this (via <code>derive newtype instance</code>) but given that the polymorphic <code>a</code> is really an unknown, I couldn't write a derivation. That's a knowledge-gap for me.</p>
<p>Finally, with these functions, I can go through the whole list and make combinations:</p>
<pre><code class="language-haskell">import Data.Array (reverse, sortBy, head, tail, concat)
import Data.Maybe (Maybe(..), fromMaybe)

makeBuySellCombinationsList :: SortedArray StockDay -&gt; SortedArray BuySell
makeBuySellCombinationsList xs = SortedArray $ reverse $ sortBy sortBuySell $ go xs ([])
  where
  go :: SortedArray StockDay -&gt; Array BuySell -&gt; Array BuySell
  go (SortedArray []) tps = tps
  go (SortedArray sdps) (tps) =
    case head sdps of
      Nothing -&gt; (tps)
      Just h -&gt; go (SortedArray $ fromMaybe [] $ tail sdps) (concat [ tps, makeBuySellList h (SortedArray $ fromMaybe [] $ tail sdps) ])

sortBuySell :: BuySell -&gt; BuySell -&gt; Ordering
sortBuySell bs1 bs2 =
  if buySellProfit bs1 &gt; buySellProfit bs2 then GT else LT
</code></pre>
<p>Worth noting that while I make this list, I am also sorting the list by profits descending. That means, I have buy-sell combinations with maximum profit at the front of the list.</p>
<p>With this (reverse) sorted list, I can now simply do this:</p>
<ul>
<li>take the highest profit-making trade</li>
<li>compare it to the next-highest profit-making trade</li>
<li>check if the day/date sequence works out. That is, the first trade sequence should have dates that are either before or after the second trade sequence in the comparison.</li>
</ul>
<p>Here's one example of valid combo:</p>
<pre><code class="language-text">((0,4), (5,5))
followed by
(1,7),(3,8)
is valid
because I buy on 4th day, sell on 5th,
then buy again on 7th day and sell on 8th.
</code></pre>
<p>The interesting thing to note is that I have to check for both directions. I could have a combination like this:</p>
<pre><code class="language-text">((1,7), (9,8))
compared with
((0,2),(3,6))
</code></pre>
<p>That is also a valid combination because you could buy on 2nd day, sell on 6th, then buy again on 7th day and sell on 8th.</p>
<p>So, basically, check both directions.</p>
<pre><code class="language-haskell">isValidTradeDayOrder :: BuySell -&gt; BuySell -&gt; Boolean
isValidTradeDayOrder (BuySell (Tuple _ a) (Tuple _ b)) (BuySell (Tuple _ c) (Tuple _ d)) =
  (a &lt; b &amp;&amp; c &lt; d &amp;&amp; c &gt; b) || (c &lt; d &amp;&amp; a &lt; b &amp;&amp; d &lt; a)
</code></pre>
<p>Before I write the function that goes through a list of sorted buy-sell pairs and fetches a possible &quot;best candidate&quot;, I need to define what a &quot;best candidate&quot; really is:</p>
<pre><code class="language-haskell">type BestCandidate = Tuple BuySell (Maybe BuySell)
</code></pre>
<p>The idea really is this -&gt; A best candidate is potentially:</p>
<ul>
<li>either a pair of two buy-sell trades</li>
<li>or just one buy-sell trade</li>
</ul>
<p>Why the second option?</p>
<p>Because according to the leetcode puzzle, you are allowed <em>at most</em> two trades in total. That means, you could also have a case where you make maximum profits with just one trade (and there is no other trade you can make which is profitable after the first).</p>
<p>Hence, the structure:</p>
<pre><code class="language-haskell">type BestCandidate = Tuple BuySell (Maybe BuySell)
</code></pre>
<p>The second <code>(Maybe BuySell)</code> is that &quot;optional&quot; buy-sell trade.</p>
<p>And now, the function that works through the whole list and picks the best candidate:</p>
<pre><code class="language-haskell">workThroughStockDays :: SortedArray BuySell -&gt; Maybe BestCandidate
workThroughStockDays tradePairs = go tradePairs 0 (Nothing)
  where
  go :: SortedArray BuySell -&gt; Int -&gt; Maybe BestCandidate -&gt; Maybe BestCandidate
  go (SortedArray []) _ candidate = candidate
  go (SortedArray tps) maxProfitSoFar candidate =
    case head tps of
      Just tp -&gt;
        let
          candidate1 = bestBuySellPair tp (SortedArray tps) maxProfitSoFar candidate
          bestCandidate = case candidate, candidate1 of
            Just c1, Just c2 -&gt; Just $ getBestCandidate c1 c2
            Just c1, Nothing -&gt; Just c1
            Nothing, Just c2 -&gt; Just c2
            _, _ -&gt; Nothing
          newMaxProfitSoFar = map candidateProfit bestCandidate # fromMaybe 0
        in
          go (fromMaybe (SortedArray []) $ map SortedArray $ tail tps) newMaxProfitSoFar bestCandidate
      Nothing -&gt; candidate
</code></pre>
<p>The logic of the <code>workThroughStockDays</code> function is this:</p>
<ul>
<li>take the first item from the BuySell list (ie, the first buy-sell pair/combination)</li>
<li>check if it has the best candidature (ie, max profits) when compared with another candidature (starting value of this candidature is <code>Nothing</code>)</li>
<li>get the profit value for the best candidate from the comparison</li>
<li>feed it recursively to the next step and do this till you run out of BuySell items in the list</li>
</ul>
<p>The best candidate comparison function is this:</p>
<pre><code class="language-haskell">bestBuySellPair :: BuySell -&gt; SortedArray (BuySell) -&gt; Int -&gt; Maybe BestCandidate -&gt; Maybe BestCandidate
bestBuySellPair buySell (SortedArray []) maxProfitSoFar bestTradeSoFar =
  if buySellProfit buySell &gt; maxProfitSoFar then (Just $ Tuple buySell Nothing)
  else bestTradeSoFar
bestBuySellPair buySell (SortedArray tradePairs) maxProfitSoFar bestTradeSoFar =
  case head tradePairs of
    Just h -&gt;
      if (isValidTradeDayOrder buySell h &amp;&amp; (buySellProfit buySell + buySellProfit h) &gt; maxProfitSoFar) then bestBuySellPair buySell (fromMaybe (SortedArray []) (map SortedArray $ tail tradePairs)) (buySellProfit buySell + buySellProfit h) (Just $ Tuple buySell (Just h))
      else bestBuySellPair buySell (fromMaybe (SortedArray []) (map SortedArray $ tail tradePairs)) maxProfitSoFar bestTradeSoFar
    Nothing -&gt;
      if buySellProfit buySell &gt; maxProfitSoFar then (Just $ Tuple buySell Nothing)
      else bestTradeSoFar


buySellProfit :: BuySell -&gt; Int
buySellProfit (BuySell a b) = fst b - fst a
</code></pre>
<p>There's a bit of a duplication there but I figured I could optimize this later.</p>
<p>Also, the <code>getBestCandidate</code> function:</p>
<pre><code class="language-haskell">getBestCandidate :: BestCandidate -&gt; BestCandidate -&gt; BestCandidate
getBestCandidate c1 c2 =
  if candidateProfit c2 &gt; candidateProfit c1 then c2 else c1

candidateProfit :: BestCandidate -&gt; Int
candidateProfit (Tuple t1 Nothing) = buySellProfit t1
candidateProfit (Tuple t1 (Just t2)) = buySellProfit t1 + buySellProfit t2
</code></pre>
<p>At this point, it looks like the pieces are in place so I can start composing them all to go from an array of integers to a best candidate!</p>
<pre><code class="language-haskell">arrayToBuySellList :: Array Int -&gt; SortedArray BuySell
arrayToBuySellList = arrayToStockDay &gt;&gt;&gt; makeBuySellCombinationsList

findBestCandidate :: Array Int -&gt; Maybe BestCandidate
findBestCandidate = arrayToBuySellList &gt;&gt;&gt; workThroughStockDays
</code></pre>
<p>One final step. Just having a <code>Maybe BestCandidate</code> is not good. I need to know what profits were made (that's the original solution).</p>
<p>So:</p>
<pre><code class="language-haskell">profitFromBestCandidate :: BestCandidate -&gt; Int
profitFromBestCandidate (Tuple tp1 Nothing) = buySellProfit tp1
profitFromBestCandidate (Tuple tp1 (Just tp2)) = buySellProfit tp1 + buySellProfit tp2
</code></pre>
<p>Now I can simply do:</p>
<pre><code class="language-haskell">totalProfits :: Array Int -&gt; Int
totalProfits = findBestCandidate &gt;&gt;&gt; map profitFromBestCandidate &gt;&gt;&gt; fromMaybe 0
</code></pre>
<p>And as a test:</p>
<pre><code class="language-bash">&gt; totalProfits [3,1,0,0,5,4,1,3]
7

&gt; totalProfits [3,1,0,0,5,4,1]
5

&gt; totalProfits [3,1,0,0]
0

&gt; totalProfits [3,1,0,0,1]
1

&gt; totalProfits [3,1,0,0,1,2,3]
3
</code></pre>
<p>The <a href="https://github.com/chandru89new/leetcode-stuff/blob/1ce02699f8f8295378291ab0a6f9001ae902fc41/src/StockProfits.purs">full source-code can be found here</a>.</p>
<p><em>Update</em>: I <a href="/max-stock-profit-leetcode-typeclass">wrote some notes on refactoring small bits of the code</a> to use a custom <code>Typeclass</code> to define what's a &quot;valid trade&quot;.</p>
]]></description>
<pubDate>Fri, 09 Jun 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Justifying a paragraph of text: Part 3</title>
<link>https://code.druchan.com/text-justify-3</link>
<guid>https://code.druchan.com/text-justify-3</guid>
<description><![CDATA[<p>Read <a href="/text-justify">part one here</a>. Read <a href="/text-justify-2">part two here</a>.</p>
<p>In the last part, I left here:</p>
<blockquote>
<p>All that remains now is getting <code>ValidLine</code>s out of the given array of words.</p>
</blockquote>
<p>I just got around to writing the function that converts an array of strings into an array of valid lines that I can then pass/process through the <code>validLineToParaLine</code> function I wrote in part two.</p>
<pre><code class="language-haskell">validLineToParaLine :: Int -&gt; ValidLine -&gt; String
validLineToParaLine maxLen (ValidLine xs) =
  specialIntercalate xs (makeSpaces maxLen (ValidLine xs))
  # Str.joinWith &quot;&quot;
</code></pre>
<p>Here's how I approached the validline generation. I was going to rely on an accumulating recursive function (very common in functional/recursive programs):</p>
<ol>
<li>The function I'm going to write will keep track of a current valid line, a final array of valid lines, and the list of words to process.</li>
<li>As a base case, if the list of words to process is empty, it's simply going to concatenate the final array of valid lines and the current valid line and return the whole thing. That will be my final valid line list!</li>
<li>If the list of words to process is not empty:</li>
</ol>
<ul>
<li>I'm going to pick the first element from the words list</li>
<li>and then I'm going to add it temporarily to the current valid line that the function is carrying around</li>
<li>and check if the total length of this temporary valid line is less than the max-length</li>
<li>if yes, I will recurse again on the function, passing as the list of words to process the tail of the list (because I've already picked out the first element)</li>
<li>if not, I will just add whatever's current valid line to the final valid lines list, and recurse on the function, passing in existing list of words (because I did not use the first element) and also emptying out the current valid line because a new valid line is going to form.</li>
</ul>
<p>In code:</p>
<pre><code class="language-haskell">listToValidLines :: Int -&gt; Array String -&gt; Array ValidLine
listToValidLines maxlen xs = helper (ValidLine []) [] xs
  where
    helper :: ValidLine -&gt; Array ValidLine -&gt; Array String -&gt; Array ValidLine
    helper (ValidLine acc) final [] = snoc final (ValidLine acc)
    helper (ValidLine acc) final ys =
      case head ys of
        Maybe.Nothing -&gt; helper (ValidLine acc) final (Maybe.fromMaybe [] $ tail ys)
        Maybe.Just wrd -&gt;
          let
            tempValidLine = snoc acc wrd
            lengthTempValidLine = totalCharLength $ ValidLine tempValidLine
          in
            if lengthTempValidLine &gt; maxlen
              then helper (ValidLine []) (snoc final (ValidLine acc)) ys
              else helper (ValidLine tempValidLine) final (Maybe.fromMaybe [] $ tail ys)
</code></pre>
<p>There's a bit of a <code>Maybe</code> wrangling because I'm using <code>head</code> and <code>tail</code>, but it's OK. The code is safer.</p>
<p>Now that I have a function that converts an array of words to an array of valid lines, I can simply map over this list to generate a list of justified lines!</p>
<pre><code class="language-haskell">justify :: Int -&gt; Array String -&gt; Array String
justify maxWidth = listToValidLines maxWidth &gt;&gt;&gt; map (validLineToParaLine maxWidth)
</code></pre>
<p><code>maxWidth</code> is the same as max length of a line.</p>
<p>I could've written it this way too:</p>
<pre><code class="language-haskell">justify :: Int -&gt; Array String -&gt; Array String
justify maxWidth xs = listToValidLines maxWidth xs # map (validLineToParaLine maxWidth)
</code></pre>
<p>Time to test:</p>
<pre><code class="language-bash">&gt; justify 16 [&quot;This&quot;, &quot;is&quot;, &quot;an&quot;, &quot;example&quot;, &quot;of&quot;, &quot;text&quot;, &quot;justification&quot;, &quot;folks,&quot;, &quot;okay?&quot;]
[&quot;This----is----an&quot;,&quot;example--of-text&quot;,&quot;justification&quot;,&quot;folks,-----okay?&quot;]
</code></pre>
<p>We can join this text with &quot;\n&quot; to get a paragraph:</p>
<pre><code class="language-haskell">justify 16 [&quot;This&quot;, &quot;is&quot;, &quot;an&quot;, &quot;example&quot;, &quot;of&quot;, &quot;text&quot;, &quot;justification&quot;, &quot;folks,&quot;, &quot;okay?&quot;] # joinWith &quot;\n&quot;
</code></pre>
<pre><code class="language-text">This----is----an
example--of-text
justification
folks,-----okay?
</code></pre>
<p>The last rule in the puzzle was:</p>
<blockquote>
<p>the last line can be left-aligned so just one space between the words is fine.</p>
</blockquote>
<p>I think there are a couple of ways to accomplish this.</p>
<p>I could have used an indexed map in <code>justify</code> function to not justify the last item in the array of valid lines.</p>
<p>Or I could simply use regex to replace all multilpe spaces (ie, one or more spaces) with just one space in the last line.</p>
<p>Those are trivial, so leaving it here for now.</p>
<hr>
<p>You can play around with <a href="https://try.purescript.org/?code=LYewJgrgNgpgBAWQIYEsB2cDuALGAnGAKEJWAAcQ8AXOABQKgjCJPMpoBEkqkA6AMRBQwSAEaw4ACgBmQsAEpWFanC49eAZRjAUAczwgIZKQGMoSAM4W4WnfsNlFpZTQCi06TBM1J7z96c2FQAVPABPWggCDRM8FDIfbABGABo4bAAmNLI0k3AYNKoYAA8qNKgUCzK4dGY0aoq0AGs0gjRmPECXVW4%2BADkYTCowsnhJM0trAaGRmC72HvUAQTw8JDCpWDRdKmw02WEoNIs0EBNc7mQw0RgLXJA0E25CtbQLCgsC9JgkMELUKDzFRqPgaKhxbZwSw2cFKBYg3gASXqUgAjhAQNUCMAgZxeppwehdLwAKpUFBQaySAijbi4xZ8K43KHWJksQjAVAYAC8cDaHTgABI4MZxvkhXAiqUJRZsCBMBKAFYQKooaQbTnFADqKDAuzgFiQ5FgABlKlRFBykNrdfreUkAGzENCDYajOAANSQFTAZpdcF5Xp9frGKzWGzBEN0lsNxpgZqqcAAXEm4GH1jCo4RY2RTeaA3AANoAImC2EqxbSxYrVaQaErcGLJSNuZgDeLIGk7alVHbytV0hQT3JD3bByad3bICa6wA-MWALrEAC0y893t1IZq1jrUNWGc7BsJ2wshFXWDln0lmO9cC2O2w27Pa9gVkl2F3u3gmp1euwxA6FAADd4BdGZ3XQKo6xMeBZXlINN3QeAUxsOUFQQ30kJXNdAJAuAwLdeBIJ4R5YO0FAowwrcUNsPQDCMddgyw584Fw0DXVmGo3hImC4FAU5dSopDk1TBAHhAXVGMQl1iCAjdMJdYIQFoJA1mo1NkRoZcAD4pIU%2BAdMzIlCDkpjFOU1SkC3TUTRgDBJCE-1igseQC0IOADVGEwUG9TT8CecwijgZypE5JoYA0MgkBg6wbLsqRHPgZz5EUDyAGJM14RUJLQHV9WLYtnQgYAbjwAB5aRIui24RL0rdDM0wg0GK0qKqqmKEvkrdkoLe99RCtckmIKgbygABhD88Fs7Z9RQxK4AalFCBGnhxsm6aH06sykpcgsDjAKApAAHVEKEFt006AGpMr6x8kFcgAGYLrGu5qSvwNqoo6hyuuE5KAJgQdvJoFDNPOurhMWqhCGYIGUBoOKMCAw7eURhapBW70JtUjb9WRy0woir7bgACRgKBRjwWr0w2MGofBundMZtN91p%2BorXC9rSfJynnrgAB9AW4Ce3lnI5onqosMmKfwPmMHQEwxsMFFyWAW4lYgFFeXcvikE54mpZ52XJBOM4%2Bfu-CpAVjX6mXJJXMkVX1eVqg7dSjyL3wIgPbO3kMGuyQ1S4xWXbgXSnq-DAkjgcmrwe92PPFrnrFBlFDPmwyaaM7Yk4N3XimmuBkbcjzYBoHWPLAAtYaHeH88L5GK886qNCgTFrD9lqPsqvPG59zGoGT6u4Gu94W7bqhTw89Am8J5Ppd5wsFzRDEaAHoex5g1v24d7Fr1Wjfie3yfXM3iKJ6nj2Ms5UUjqQcGaR%2BGh7%2BLZdiwTuAMuHNlL8Tn3sy8j5KAfk8ABW4MhVMsg1hQEOnwFm4YzqZ1Zog3SWckAAK8EAkBYCgohTCB3SUrx3ggCvIWZyaR8HLy-g8YcxAKhVCUolFOGk06oOQZGIk4Ms6JUIPQqgjDfoulitaLYfNeS4BllTH620iwLlckvZ6OscBezgE3CRvM5qCIMmwhBGcdEZg4ZCJBuitFqKNlI%2Ba0UTCuUHGgW8CjeSmxMHAWxt5pHSXgFYj%2B6jjaWJMNYlx6Bbz4IDE3DyTwry4F%2BHAEJh4wkezZLwPomJyxGN0j4ixWioT%2BJsUEw6khEnSAMMANksiJQ8ApDEly8SPKJIAFIqhoJgPAVdDI1I9mXdp-dtBkHmo404zirFYBaV0jpdkHzBB6X0-eWN1rjP1MKeaRRyA8J9msriqj1nrKDrdSZKysm6U1FsUZ-dcAYAyVtDxsiHZOMCXY-JfjrGuXwScj2sd4AXPcfpSUUytG5PuVIQpxTSkKOFBUw6%2BDLSEH7OSdUtVmZZ0MboLh7Djy6GhY0tUGprS-jtHec0AjtrCJtH%2BMO2lDlIFFKZDxSkVJqWEj%2BW02AoXECAA">the full-code here</a>.</p>
]]></description>
<pubDate>Thu, 08 Jun 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Justifying a paragraph of text: Part 2</title>
<link>https://code.druchan.com/text-justify-2</link>
<guid>https://code.druchan.com/text-justify-2</guid>
<description><![CDATA[<p>Read <a href="/text-justify">part one here</a>.</p>
<p>In the first part, I had this problem to fix:</p>
<blockquote>
<p>The trick is to find out how to go from a &quot;number of spaces to distribute&quot; to a &quot;special spaces array&quot;.</p>
</blockquote>
<p>I worked through a few examples to get an intuitive feel of how the logic could look like:</p>
<p>Suppose there are 7 spaces to distribute in 3 slots:</p>
<pre><code class="language-text">spaces to distribute        -&gt; 7
slots                       -&gt; 3
</code></pre>
<p>I could first distribute equally by dividing the two (integer division):</p>
<pre><code class="language-text">equal division        -&gt; 7 / 3 = 2 (remainder 1)
                      -&gt; [&quot;--&quot;, &quot;--&quot;, &quot;--&quot;]
</code></pre>
<p>And then distribute the <code>remainder</code> left-to-right:</p>
<pre><code class="language-text">equal division        -&gt; 7 / 3 = 2 (remainder 1)
                      -&gt; [&quot;--&quot;, &quot;--&quot;, &quot;--&quot;]
with remainder        -&gt; [&quot;---&quot;, &quot;--&quot;, &quot;--&quot;]
distributed
</code></pre>
<p>I tried to run some more examples for this. Like this:</p>
<pre><code class="language-text">spaces to distribute  -&gt; 15
slots to distribute   -&gt; 4
equal distribution    -&gt; 15/4 =&gt; 3
                      -&gt; [&quot;---&quot;,&quot;---&quot;,&quot;---&quot;,&quot;---&quot;]
remainder             -&gt; 3
remainder distributed -&gt; [&quot;----&quot;,&quot;----&quot;,&quot;----&quot;,&quot;---&quot;]
</code></pre>
<p>This still did not give me a clear grasp of the distribution logic.</p>
<p>Eventually, I came up with this idea: I could have a function that gradually &quot;builds&quot; the array of spaces from an empty array.</p>
<p>It would do this by &quot;remembering&quot; and &quot;updating&quot; the number of extra/remainder spaces it has to distribute across the slots.</p>
<p>So, something like this:</p>
<pre><code class="language-text">example: 15 spaces, 4 slots

step 1: empty array ([]), number of slots to fill (4), number of remainders to distribute (remainder 15/4 = 3)
step 2:
  - push the first space slot into the array [?]
  - where ? = normally, just the equal distribution of space.
  - in this case, that's 15/4 quotient = 3. so =&gt; [3]
  - but the number of remainders to distribute is more than 0,
  - so we just add 1 more to the fill =&gt; [4]
  - now that we've filled the first, we have to reduce number of slots to fill to 3.
  - also, we've distributed 1 of the 3 remainders to distribute so that also becomes 2.
step 3:
  - repeat the above step for the next slot,
    but remember that we only have 2 remainders to distribute and 3 slots to fill.
...and so on...
</code></pre>
<p>Doing this &quot;recursively&quot; gives us a final array of space slots.</p>
<p>The function looks something like this:</p>
<pre><code class="language-haskell">import Prelude

import Data.Array (snoc, catMaybes)
import Data.Int (quot, rem)
import Data.String.Utils (repeat)

makeSpaces :: Int -&gt; ValidLine -&gt; Array String
makeSpaces maxLen vl =
  let
    d = deficit maxLen vl
    spaceSlots = numberOfSpaces vl
    totalSpaces = d + spaceSlots
  in
    makeSpacesHelper [] (quot totalSpaces spaceSlots) (rem totalSpaces spaceSlots) spaceSlots
    # map (\a -&gt; repeat a &quot;-&quot;)
    -- note: I'm using hyphens for demonstration
    -- use space in the real program
    # catMaybes

makeSpacesHelper :: Array Int -&gt; Int -&gt; Int -&gt; Int -&gt; Array Int
makeSpacesHelper xs _ _ 0 = xs
makeSpacesHelper xs n incCount timesCount =
  makeSpacesHelper (snoc xs a) n (incCount-1) (timesCount-1)
    where
    a = n + (if incCount &gt; 0 then 1 else 0)
</code></pre>
<p>In this:</p>
<pre><code>incCount =&gt; the remainders to be distributed
timesCount =&gt; number of slots to be filled
</code></pre>
<p>I've used a bunch of helper functions in the above code like <code>deficit</code>, <code>numberOfSpaces</code> and some library functions like <a href="https://pursuit.purescript.org/"><code>catMaybes</code></a>.</p>
<p>Here's definitions for the helpers:</p>
<pre><code class="language-haskell">import Data.Array (length, foldl)
import Data.String as Str

deficit :: Int -&gt; ValidLine -&gt; Int
deficit maxLen vl = maxLen - (totalCharLength vl)

numberOfSpaces :: ValidLine -&gt; Int
numberOfSpaces (ValidLine xs) = length xs - 1

totalCharLength :: ValidLine -&gt; Int
totalCharLength (ValidLine xs) =
  foldl (\b a -&gt; b + Str.length a) 0 xs + numberOfSpaces (ValidLine xs)
</code></pre>
<p>Testing all of this:</p>
<pre><code class="language-bash">&gt; makeSpaces 16 (ValidLine [&quot;this&quot;,&quot;is&quot;,&quot;an&quot;])
# that is max length is 16. valid line has char length = 10.
# deficit = 6. Total spaces to distribute is 6+2 = 8. Across 2 slots.
[&quot;----&quot;,&quot;----&quot;]

&gt; makeSpaces 19 (ValidLine [&quot;a&quot;,&quot;b&quot;,&quot;c&quot;,&quot;d&quot;,&quot;e&quot;])
# that is, max length is 19, valid line has 9 characters length, so deficit is 10.
# Total spaces to distribute is 10+4 = 14. Across 4 slots.
[&quot;----&quot;,&quot;----&quot;,&quot;---&quot;,&quot;---&quot;]
</code></pre>
<p>All we need now is a way to mix an array of strings (from the <code>ValidLine</code>) and this spaces array such that the words and spaces are interleaved.</p>
<p>I checked Purescript's Array package and found a <code>transpose</code> function that fit the need perfectly:</p>
<pre><code class="language-haskell">transpose :: forall a. Array (Array a) -&gt; Array (Array a)

{-
The 'transpose' function transposes the rows and columns of its argument. For example,

transpose
  [ [1, 2, 3]
  , [4, 5, 6]
  ] ==
  [ [1, 4]
  , [2, 5]
  , [3, 6]
  ]

If some of the rows are shorter than the following rows, their elements are skipped:

transpose
  [ [10, 11]
  , [20]
  , [30, 31, 32]
  ] ==
  [ [10, 20, 30]
  , [11, 31]
  , [32]
  ]
-}

</code></pre>
<p>So I could just put the array of valid lines and array of space slots into another array and then <code>transpose</code> them.</p>
<pre><code class="language-haskell">import Data.Array (transpose, concat)

specialIntercalate :: forall a. Array a -&gt; Array a -&gt; Array a
specialIntercalate xs ys = transpose [xs, ys] # concat
</code></pre>
<p>Mixing these so far:</p>
<pre><code class="language-haskell">validLineToParaLine :: Int -&gt; ValidLine -&gt; String
validLineToParaLine maxLen (ValidLine xs) =
  specialIntercalate xs (makeSpaces maxLen (ValidLine xs))
  # Str.joinWith &quot;&quot;
</code></pre>
<p>And the test:</p>
<pre><code class="language-bash">&gt; validLineToParaLine 19 (ValidLine [&quot;a&quot;,&quot;b&quot;,&quot;c&quot;,&quot;d&quot;,&quot;e&quot;])
&quot;a----b----c---d---e&quot;

&gt; validLineToParaLine 16 (ValidLine [&quot;This&quot;,&quot;is&quot;,&quot;an&quot;])
&quot;This----is----an&quot;
</code></pre>
<p>All that remains now is getting <code>ValidLine</code>s out of the given array of words.</p>
<p>I'll write about that in the next part.</p>
]]></description>
<pubDate>Wed, 07 Jun 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Justifying a paragraph of text: Part 1</title>
<link>https://code.druchan.com/text-justify</link>
<guid>https://code.druchan.com/text-justify</guid>
<description><![CDATA[<p>Following-up on the <a href="/int-to-roman">leetcode stuff I was doing recently</a>, I decided to pick up another Leetcode problem. This time, <a href="https://leetcode.com/problems/text-justification/">text justification algorithm</a>.</p>
<p>Quick rundown of the problem:</p>
<ul>
<li>you're given an array of words (eg <code>[&quot;This&quot;, &quot;is&quot;, &quot;an&quot;, &quot;example&quot;, &quot;of&quot;, &quot;text&quot;, &quot;justification.&quot;]</code>)</li>
<li>and a max-length per line (eg <code>16</code>)</li>
<li>and you have to space the words in such a way that the resulting text/paragraph is &quot;justified&quot;.</li>
</ul>
<p>The rule for spacing is simple:</p>
<ul>
<li>one space between words</li>
<li>if that does not fit the line (length of line = 16), introduce more spaces between words</li>
<li>more spaces get distributed left to right so spaces on the left would get filled / incremented first and then the right.</li>
<li>the last line can be left-aligned so just one space between the words is fine.</li>
</ul>
<p>I spent a few minutes trying to find out how to extract the collection of words that would fit a line.</p>
<p>That is, if this is the text:</p>
<pre><code class="language-text">[&quot;This&quot;, &quot;is&quot;, &quot;an&quot;, &quot;example&quot;, &quot;of&quot;, &quot;text&quot;, &quot;justification.&quot;]
</code></pre>
<p>and the max length per line is 16,</p>
<p>the first line can only have <code>[&quot;This&quot;, &quot;is&quot;, &quot;an&quot;]</code>.</p>
<p>This is because if I include the word &quot;example&quot;, then the length of this line exceeds 16. Remember that I have to include spaces between the words as well in the calculation of line length.</p>
<p>I decided to park this and assume there's a function that gives me a &quot;valid&quot; line already and work on calculating the number of spaces to distribute to justify a &quot;valid&quot; line.</p>
<p>(A valid line is basically an array of words that can fit <em>within</em> the max-length per line including at least 1 space between each word).</p>
<p>In this example, some valid lines would be:</p>
<pre><code class="language-text">valid lines:
[&quot;This&quot;, &quot;is&quot;, &quot;an&quot;]
[&quot;example&quot;, &quot;of&quot;, &quot;text&quot;]
</code></pre>
<p>I decided to use a <code>newtype</code> to differentiate between any random array of words and a valid line:</p>
<pre><code class="language-haskell">newtype ValidLine = ValidLine (Array String)
</code></pre>
<p>What I have to get to, as a first step, is this function:</p>
<pre><code class="language-haskell">validLineToParaLine :: Int -&gt; ValidLine -&gt; String
validLineToParaLine maxLen validLine = ? -- to be implemented
</code></pre>
<p>After a while of thinking about this, here's one logic I came up with:</p>
<ul>
<li>the actual length of the line is length of each word in the valid line, and spaces between them.</li>
<li>the total number of spaces (single-space) in a valid line array is simply one less than the total number of words in the valid line array.</li>
<li>if I subtract the actual length of the valid line from the deficit, I get the number of extra spaces I have to distribute.</li>
</ul>
<p>This gives me the number of spaces to distribute.</p>
<p>Here's an example of the logic at work:</p>
<pre><code class="language-text">valid line        -&gt; [&quot;This&quot;, &quot;is&quot;, &quot;an&quot;]
spaces            -&gt; total words in valid line (3) minus 1 = 3-1 = 2
total characters  -&gt; sum of length of each word in valid line (4+2+2=7) + spaces(2) = 8+2 = 10
deficit           -&gt; max length (16) minus total chars (10) = 16-10 = 6
</code></pre>
<p>Okay, so now I have &quot;extra number of spaces to distribute&quot; in the line.</p>
<p>But it got tricky here as I had to find out how to distribute <code>x</code> number of spaces across <code>y</code> number of space-slots.</p>
<p>That is:</p>
<pre><code class="language-text">deficit = 6
6 spaces have to be distributed in this line: &quot;This is an&quot;.
</code></pre>
<p>Visually, it looks easy. There are just 2 space-slots. Divide 6 by 2 = 3. So each slot gets 3 extra spaces. (Will replace space with hyphen to be more clear in the representation)</p>
<pre><code class="language-text">This----is----an
</code></pre>
<p>But what is the general logic here?</p>
<p>After much thought, I came up with what looks like a slightly-complicated solution.</p>
<p>Here's the logic:</p>
<ol>
<li>First off, instead of using the &quot;deficit&quot; (that is, how many <em>more</em> spaces need to be distributed besides the usual number of spaces), I combined the deficit with existing spaces so that now, I just have to worry about how many spaces to distribute in total between the words.</li>
<li>Using the &quot;total spaces to distribute&quot; number, I construct another array that represents the number of spaces to actually put between each word in the valid line.</li>
</ol>
<p>Here's an example of point #2:</p>
<pre><code class="language-text">valid line          -&gt; [&quot;This&quot;, &quot;is&quot;, &quot;an&quot;]
total spaces to     -&gt; usual spaces (2) + deficit (6) = 8
distribute
space distribution  -&gt; [&quot;----&quot;,&quot;----&quot;]
array
</code></pre>
<p>If the total spaces to distribute was 9, then the array would look like this:</p>
<pre><code class="language-text">[&quot;-----&quot;,&quot;----&quot;]
</code></pre>
<p>That is, the first item will have 5 spaces and the second will have 4.</p>
<p>If I have this array, I could simply merge these two arrays in some way to get the final result:</p>
<pre><code class="language-text">valid line        -&gt; [&quot;This&quot;, &quot;is&quot;, &quot;an&quot;]
spaces array      -&gt; [&quot;----&quot;,&quot;----&quot;]

justified         -&gt; [&quot;This&quot;,&quot;----&quot;,&quot;is&quot;,&quot;----&quot;,&quot;an&quot;]
</code></pre>
<p>The trick is to find out how to go from a &quot;number of spaces to distribute&quot; to a &quot;special spaces array&quot;.</p>
<p>I'll post about that in the second part.</p>
]]></description>
<pubDate>Tue, 06 Jun 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Integer to Roman: Purescript version</title>
<link>https://code.druchan.com/int-to-roman</link>
<guid>https://code.druchan.com/int-to-roman</guid>
<description><![CDATA[<p>Decided to pick a Leetcode puzzle last night to solve in Purescript.</p>
<p>I did a couple of them but here's one that I liked. <a href="https://leetcode.com/problems/integer-to-roman/">Converting an integer (under 3999) to Roman numerals</a>.</p>
<p>The basic rules are these:</p>
<ol>
<li>Roman numerals are represented by seven different symbols: <code>I</code>, <code>V</code>, <code>X</code>, <code>L</code>, <code>C</code>, <code>D</code> and <code>M</code>.</li>
<li>There's a table on the puzzle page that shows what these symbols mean. For the most part, it's simple. Things change at 5, 10, 50, 100, 500 and 1000.</li>
<li>And then there are special rules for 4, 9, 40, 90, 400 and 900 which are represented with a slightly different algorithm. (Eg, 4 is not <code>IIII</code>, it's <code>IV</code> and 40 follows a similar logic, so it's <code>XL</code> – that is, 10 less than 50).</li>
</ol>
<p><strong>The basic idea:</strong></p>
<p>Say, the number to convert into Roman is 23.</p>
<p>I could split that as 20 + 3 (so we know what numbers show up in 1s, 10s ... and so on places).</p>
<p>Then, I can &quot;translate&quot; the numbers like so:</p>
<pre><code class="language-text">23 -&gt; [2x10, 3x1]
      [XX, III]
      [XXIII]
</code></pre>
<p>But if I have a 4 or a 9 somewhere, I need to use a slightly different representation:</p>
<pre><code class="language-text">49 -&gt; [4x10, 9x1]
      [XXXX, IX] -&gt; wrong
      [XL, IV] -&gt; correct
      [XLIV]
</code></pre>
<p>So for each place (1s, 10s, 100s, 1000s), there are special rules for the numbers 4, 5 and 9.</p>
<p>Come to think about it, there are also special rules for numbers greater than 5 in each place.</p>
<p>In 1s place, any number greater than 5 but less than 9 is represented as (Roman for 5) + (Roman for difference).</p>
<p>Eg, 7.</p>
<pre><code class="language-text">7 -&gt; greater than 5 by 2.
  -&gt; Roman for 5 + Roman for 2
  -&gt; V II
  -&gt; VII
</code></pre>
<p>The same kind of a rule applies for 70 too but now, the application changes a little:</p>
<pre><code class="language-text">70  -&gt; greater than 50 by 20
    -&gt; Roman for 50 + Roman for 20
    -&gt; L XX
    -&gt; LXX
</code></pre>
<p>So I realized that the best thing to do (given the limitation that the larger number to convert could only be 3999) was to just write replacement rules for each place in the decimal system.</p>
<p>Here's the replacement rule for the unit (1s) place:</p>
<pre><code class="language-haskell">import Prelude
import Data.Maybe as Maybe
import Data.String.Utils (repeat)

process1sPlace :: Int -&gt; String
process1sPlace x
  | x == 4 = &quot;IV&quot;
  | x == 5 = &quot;V&quot;
  | x == 9 = &quot;IX&quot;
  | x &lt; 5 = repeat x &quot;I&quot; # Maybe.fromMaybe &quot;&quot;
  | x &gt; 5 = &quot;V&quot; &lt;&gt; (repeat (x - 5) &quot;I&quot; # Maybe.fromMaybe &quot;&quot;)
  | otherwise = &quot;&quot;
</code></pre>
<p>As you can see, the logic is kind of straightforward:</p>
<ul>
<li>for 4, 5 and 9, we have special cases. I just directly convert them into their corresponding roman numeral,</li>
<li>for anything less than 5, I just repeat <code>I</code> as many times,</li>
<li>and for anything more than 5, I prepend a <code>V</code> and then repeat <code>I</code> as many times as the difference.</li>
</ul>
<p>The <code>repeat</code> function from <code>Data.String.Utils</code> returns a <code>Maybe</code>, which explains why I use a <code>fromMaybe</code> to unbox the data.</p>
<p>These are the functions for the rest of the places:</p>
<pre><code class="language-haskell">process10sPlace :: Int -&gt; String
process10sPlace x
  | x == 1 = &quot;X&quot;
  | x == 4 = &quot;XL&quot;
  | x == 5 = &quot;L&quot;
  | x == 9 = &quot;XC&quot;
  | x &lt; 5 = repeat (x) &quot;X&quot; # Maybe.fromMaybe &quot;&quot;
  | x &lt; 10 = &quot;L&quot; &lt;&gt; (repeat (x-5) &quot;X&quot; # Maybe.fromMaybe &quot;&quot;)
  | otherwise = &quot;&quot;

process100sPlace :: Int -&gt; String
process100sPlace x
  | x == 1 = &quot;C&quot;
  | x == 5 = &quot;D&quot;
  | x == 4 = &quot;CD&quot;
  | x == 9 = &quot;CM&quot;
  | x &lt; 5 = repeat x &quot;C&quot; # Maybe.fromMaybe &quot;&quot;
  | x &gt; 5 = &quot;D&quot; &lt;&gt; (repeat (x-5) &quot;C&quot; # Maybe.fromMaybe &quot;&quot;)
  | otherwise = &quot;&quot;

process1000sPlace :: Int -&gt; String
process1000sPlace x = repeat x &quot;M&quot; # Maybe.fromMaybe &quot;&quot;
</code></pre>
<p>Note that for the 1000s place, I just repeat <code>M</code>. We're not dealing with numbers greater than 3999 so this solution works.</p>
<p>Well, now I have the logic to process each number based on which place it is on the decimal system but that leaves me with one other problem: How do I actually split a number into its corresponding number + decimal place?</p>
<p>As an example: 437.</p>
<pre><code class="language-text">437  -&gt; 4x100s, 3x10s, 7x1s
</code></pre>
<p>Turns out, this one I'll have to work in reverse.</p>
<ol>
<li>If I divide 437 by 10, I get 43 as the quotient and 7 as the remainder. Hurray, I was able to &quot;extract&quot; 7 out.</li>
<li>If I repeat the division by 10 on the quotient 43, I now get 4 as the quotient and 3 as the remainder! Hurray again: I've extracted the 3 out.</li>
<li>Repeat this again and I'm left with 0 as quotient and 4 as remainder -&gt; i.e, extracted the 4 out too!</li>
</ol>
<p>But wait, I also have to remember which decimal place each number belonged to.</p>
<p>I could do this by <em>keeping track</em> of the number of times I'm dividing by 10. The first time, it's 1s place, the second time, it's the 10ths place and so on.</p>
<p>The logic seems okay but I have to think of a nice data structure that can hold this information. (Side quote: <em>Good programmers worry about data structures and their relationships.</em> - Linus Torvalds)</p>
<p>I thought a Tuple would be best. So:</p>
<pre><code class="language-text">437 -&gt; [ (4,100), (3,10), (7,1) ]
</code></pre>
<p>seemed like a nice representation that I can work with. (Remember the place functions above: I can use the Tuple to know which place function to pass the number through!)</p>
<p>So here's the code I wrote to represent the data and also split the number into the data type:</p>
<pre><code class="language-haskell">import Data.Tuple as Tuple
import Data.Int (rem, quot, pow)
import Data.Array (snoc, reverse)

type Group = Tuple.Tuple Int Int -- the data structure

splitter :: Int -&gt; Array Group
splitter x = reverse $ go (quot x 10) (rem x 10) 0 []
  where
    go :: Int -&gt; Int -&gt; Int -&gt; Array Group -&gt; Array Group
    go quotient remainder power acc
      | quotient == 0 = snoc acc (Tuple.Tuple remainder (pow 10 power))
      | otherwise = go (quot quotient 10) (rem quotient 10) (power + 1) (snoc acc (Tuple.Tuple remainder (pow 10 power)))
</code></pre>
<p>The <code>splitter</code> takes a number and starts doing the logic I discussed above. Divide by 10, save the remainder and the decimal value (by using the power function) and repeat till the quotient is 0.</p>
<p>And finally it <code>reverse</code>s the list (because I worked backwards).</p>
<p>As a test run:</p>
<pre><code class="language-bash">&gt; splitter 437
[(Tuple 4 100),(Tuple 3 10),(Tuple 7 1)]
</code></pre>
<p>Okay, now I just have to take each Tuple and then:</p>
<ul>
<li>use the second part of the tuple to find out which place function to use</li>
<li>and use the first part of the tuple as input for the place function</li>
</ul>
<pre><code class="language-haskell">groupToRoman :: Group -&gt; String
groupToRoman (Tuple.Tuple num place)
  | place == 1 = process1sPlace num
  | place == 10 = process10sPlace num
  | place == 100 = process100sPlace num
  | place == 1000 = process1000sPlace num
  | otherwise = &quot;&quot;
</code></pre>
<p>At this point, just wanted to note how amazing the destructuring and guard syntaxes are in Purescript/Haskell to be able to write such succint functions that read like math expressions easily.</p>
<p>Now that I have this function to process a Tuple, I can use <code>foldr</code> to simply walk over a list of tuples and join them:</p>
<pre><code class="language-haskell">-- all other imports
import Data.Array (snoc, reverse, foldr) -- modified to add `foldr`

arabicToRoman :: Int -&gt; String
arabicToRoman x =
  splitter x
    # foldr fn &quot;&quot;
    where
    fn grp acc = groupToRoman grp &lt;&gt; acc
</code></pre>
<p>And to test:</p>
<pre><code class="language-bash">&gt; arabicToRoman 437
CDXXXVII

&gt; arabicToRoman 3789
MMMDCCLXXXIX

&gt; arabicToRoman 7
VII
</code></pre>
<p>And that's it.</p>
<p>The fun bits was trying to break the logic into small chunks that can be expressed cleanly in Purescript and then the composition.</p>
<p><strong>Behind the scenes</strong></p>
<ul>
<li>I initially thought I could use a look-up table for the 4, 9, 40, 90... special cases. But that seemed to create more complexities.</li>
<li>The lookup table also failed because the rules change at the 5 mark (5, 50, 500) for each place: it becomes a representation of Roman for 5/50/500 plus the roman for the difference.</li>
<li>Also, a lookup table would've introduced a lot more <code>Maybe</code> unwrappings, which could clutter the code.</li>
</ul>
<p>You can see/hack around with the <a href="https://try.purescript.org/?code=LYewJgrgNgpgBAWQIYEsB2cDuALGAnGAKEJWAAcQ8AXOABQKgjCJPMpoFEAzLmAYxoAKbrwEBKVhWpwAKngCetCAQDKfPCjJDsARgA0cbACYDZA1RgAPKgagoAzjbjpmaJ3bQBrAwTTM8BnzgMBKkUjQAIkhUSAB0AIJ4eEjycIL2aCB8BlwgUGABcAQAbvj2IZLscFExscjyAEbwSPaIKU2V0jVxAJJuaQTABgCOECBOFJihbF3RcSpUGmgA5rEAqlQoUK2CBGQw0dPh1XOxMhBksHAtshewhAC0D84zkacAYnlgSA1Xgrn5PASQjAVAYABc4LgIn4NDWaBQVBBYLgAF5CHAijA-Pg4AASQw6NIWaz467JBooPgyEAAJRAoIwAGYAOwADgAnBJMU8sTi8GTdMSrDQCfZsCBMGT7JdERYBQAWVncuCEMh4LIwez2HT2WhQJB8eCQuB9GgPAB8cAWS2Wao1Ru1uv1hvgllVmIAPnB3ajUXAFWi4AAiHoANWDGLg3t9-oArEHgxGozG0f6OYmegANSNen1wAA8cAT-r2Bxo7tDwbgAGI2o0YLEuBrgPUmiHc9H81aSyGI4WrbsYPtoml3c842IQz1q3W243mwz5x2Vd7xrg8JgHPB-cHI-bNU6AAx6g1GuAms1wS3WxboO3qw86k8u8-ulP5v1wIm7nMf2MBomWYADKdqmX69sGoH-mmcAZr%2BADCYH5kWvZlqOgiWFOwY5rW9ZNE2LbLnuMFFjoR6JqBA4DMO5Zjg8k4hrhc7tAuRGsSuH7rvgW7lImnYHo6z4vmexpQleN42veglasJp6uvmMFfj%2BIZIUp8aJhEyEAYGu4IVp6lwYmCEINphbFkG6EVqps74WxS4cSRebuj2mnVgWg5WfRjHBkheHzoRDkNpxebcZu278VGMnHkeIkKZe-SSXeKzRcJcVvpZtGjpWpn%2BaxgWto5%2B5UPI%2BxwAA4hqFxBuclyNrVVxXmaxAynYVDyhe4mJVaiTJKklUgBchCtXKuK%2BlipR4HxBLLCAaSjOM%2BbkVOQ7AEtR5ThRADaAC6UY4Pg8BRpis2daa3XneaVoST1SQpBVVVkNet19Q9g1kMdcCnQtmzYjQgxgv4cCTLihp8B6mKQ96P0oH9sEUf6GRZNcfDg4IDX1Xc8AAy4uKCJM34USDQIqpDXZhbxO5fXNgg-XAMNw8tNFrQz-RM-jkq4gA1N%2BK1I%2BDYNpBjZxY1ioK4wKHNSuRwOcyTKqfYQyyPTS9KMmdA3VUltpKyrdIMkgGDo1jIt1XAaAQGtlyuquwOibBKmPkJzr2xbwAftb57KQjwMOrJ5Hyeebse-b3s%2B07-uxYH8DB3mns7v65GxUGEcxelMeW1xVAbpTkWYoQSAUlSqsGxCXVXbeOuFz8xf6%2Br41RiN7VjZ9dYAgUcBcBgTmQwdBCfV3X14E9gv%2Bsr70l%2BrytPR5KN8Ir48XDSUkrBrj3PZX0mL2Qy-JcsQsmxj1xwA0U67oI7lWuKkrH7PwYGJfcDX1KDTUcGYidvnvLb71KS77aZ1f79XXtraS39HpAP-veH0rR-TtygJ3bu1ZLD2EeM8TEfd4CD1fkgIM28oGr1wXfSEj8GiKzJuQyGn0gA">full source here</a>.</p>
]]></description>
<pubDate>Mon, 05 Jun 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Summer of Elm</title>
<link>https://code.druchan.com/summer-of-elm</link>
<guid>https://code.druchan.com/summer-of-elm</guid>
<description><![CDATA[<p>It's very likely that I'll come off as an <a href="https://elm-lang.org">Elm</a>-fanboy in these notes. I don't think of myself as one for I've seen some of the limitations of this beautiful language, but I have to confess I do become quite enamoured when writing about (or writing in) the language.</p>
<p>After a few-months-long hiatus, I seem to have suddenly landed on a purple patch of writing a lot of Elm. It began as a self-imposed feature request on an old project I had done where you could <a href="https://lwc-druchan.vercel.app/">visually see a calendar of potential long-weekends</a>. I realize I sound vain but I seem to have surprised myself in being able to stick to doing things this long and in such quantities. All tallied, I modified 2 old projects written in Elm (<a href="https://github.com/chandru89new/xpns">one of which is my go-to expense tracker</a> that I use almost everyday), created <a href="https://github.com/chandru89new/es-imports-parser">a tool</a> that helps me in my daily work, published <a href="https://package.elm-lang.org/packages/chandru89new/elm-simple-cli-options-parser/latest/">my first Elm package</a>, and managed to wire up <a href="https://github.com/chandru89new/harbor">something that can serve as a pattern library</a> to interact type-safely with <a href="https://guide.elm-lang.org/interop/ports.html">Elm's Ports</a> (something that's currently not easily done).</p>
<p>My foray into this strange world of &quot;functional programming&quot; really began in 2018. I suspect the actual history is permanently lost in my neural pathways but I remember the dichotomy of having been singed by Javascript frameworks (Vue was, at the time, my favorite and also the one I had the most experience with) prompting me to seek alternatives (leading to the beautiful little website of Elm Lang) and the impregnable-esque feeling one finds oneself in when staring at <code>Maybes</code> and <code>Results</code> and <code>Json Decoders</code>.</p>
<p>It's only recently that I've realized how lucky it is that I discovered Elm not at the time of its initial release but a few years later, because by 2018, Elm had undergone a sort of a major overhaul in terms of the entire structure, philosophy and architecture. It would undergo one more transformation a little later (with v0.19) but honestly, since none of the work I do involves Elm at large-scale production, that drama did not affect me. Anyway: having seen a good chunk of code written in pre-2018-style Elm, I can safely say I'd have easily given this language a miss if I had to deal with that for runtime safety guarantees.</p>
<p>My abortive attempts at learning Elm at the time were a chagrin. I was frustrated that I couldnt quite &quot;intuit&quot; the idioms of the language and yet I wanted to learn this language because I was thoroughly convinced – because of my interactions with libraries like RamdaJS – that this functional-style of writing code was a lot better than anything I had done (or was doing at work).</p>
<p>Late 2019, however, something happened: I had to model a customized <a href="https://www.highcharts.com">Highcharts</a> implementation (think massive, highly-user-customizable wrappers around that charting library), and this would've been extremely complex. What saved the day was that I wrote all of the first-pass code in pseudo-Elm (ie, Elm-like syntax, but code that wont really run). But even the pseudo-code suddenly made it extremely easy to write &quot;composable&quot; chunks of manageable, grokkable code that led directly to the larger model. Transfering all of that into Vue / JS was a breeze (although just tedious because so many functions!). I was completely &quot;sold&quot; at this point to the notion that Elm would make me enjoy programming far more than I had ever did so far (and I was already enjoying writing programs).</p>
<p>Cut to 2023: It's a pity that I have not put in enough time/effort in looking for external projects where I could work with Elm completely, but I find this language to be a refuge nevertheless. My forays into Purescript (and a bit of Haskell) has made me realize that there's a lot more to the iceberg than meets the eye (and Elm really is sort of a tip-of-the-iceberg thing in the functional world – all of it by choice because it aims to be a frontend language right now and wants to make the transition from unsafe JS to safe functional-style programming as easy as possible). For instance, pun intended, <code>Type classes</code> and <code>instance</code>s are very much missing from Elm (for good reasons) and they are quite powerful tools when programming applications. Despite these, Elm continues to be quite a powerful toolkit to build complex stuff.</p>
]]></description>
<pubDate>Mon, 15 May 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Once more unto the Vim, dear friends.</title>
<link>https://code.druchan.com/once-more-unto-the-vim-dear-friends</link>
<guid>https://code.druchan.com/once-more-unto-the-vim-dear-friends</guid>
<description><![CDATA[<p>Ages ago, I fell in love with Vim just because I knew how to quit the editor with :q or :wq and could switch between insert and normal modes. But of course like all “beginner’s luck” tales, this ended badly, exacerbated by the fact that I turned on vim-bindings on the IDE I was using at the time too enthusiastically with no clue of all the different problems that this would have brought along. Mostly because I didnt know over 90% of Vim’s grammar, and muscle memory was all about classic IDE-style shortcuts.</p>
<p>Now, almost two years later, I have repeated half the feat but this time, I went in a little more prepared. I found <a href="https://href.li/?https://www.barbarianmeetscoding.com/boost-your-coding-fu-with-vscode-and-vim/dedication">a great resource</a> that covered the 80% use-case first (while occasionally showing you a glimpse of the 20% use-case as well) and practiced somewhat diligently.</p>
<p>I dont know what constitutes success but I’m now at a comfortable point in using Vim-style actions. But I won’t switch to Vim or Neovim because I’m too tied to VS Code and the plugins there. So I have <a href="https://href.li/?https://marketplace.visualstudio.com/items?itemName=vscodevim.vim">this plugin</a> that makes Vim out of VS Code so it’s kind of the best of both worlds.</p>
<p>The best bit though is that I’ve gotten a little too used to the efficacy of the Vim grammar of shortcuts (while occasionally shifting to the classic Ctrl/Cmd sequences) that I <em>want</em> to use these bindings in every text-editor interface. And that’s why my Obsidian interface now has Vim-style bindings enabled. Some small misgivings aside, great experience.</p>
<p>If you were a little too obsessive, you might also seek some similar way of navigating between tabs, searching on a page, navigating through history, and so on for your browser. And that’s when you’ll find <a href="https://t.umblr.com/redirect?z=https%3A%2F%2Fchrome.google.com%2Fwebstore%2Fdetail%2Fvimium%2Fdbepggeogbaibhgnhhndojpepiihcmeb%3Fhl%3Den&amp;t=ZjRmYTQzZmEwYWQ0NzM5Nzc0MmNiNWE5YmRkMmYzOTY4OGQ4YzUyOSw2OGYxMmM0YjBiMmRjOWVlZWIxOWE3YzA0MmFhODYxNTk2NTk1MjQy&amp;ts=1678895749">Vimium</a>.</p>
]]></description>
<pubDate>Wed, 15 Mar 2023 12:00:00 +0530</pubDate>
</item><item>
<title>Building a useInterval hook from scratch</title>
<link>https://code.druchan.com/use-interval-hook</link>
<guid>https://code.druchan.com/use-interval-hook</guid>
<description><![CDATA[<p><strong>TL;DR</strong>:</p>
<ul>
<li>running a function repeatedly, at set intervals, is tricky in React. existing examples and libraries are all-too simple for real-world use cases (for eg, they dont work great for async functions, they dont do well with exponential backoff and they dont stop when the function being called at regular interval fails)</li>
<li>you might reach for setInterval at first, but it has problems like waiting for an async function to finish before calling it again at a given interval, or to stop after a few failed calls. complexity compounds when you add a backoff to this.</li>
<li>what we'll end up doing in this exercise is to build on top of two critical things – setTimeout and useEffect's &quot;unmounting&quot; behavior – to build a decently-robust useInterval hook that works great for both regular and async functions, and comes with a couple of extra niceties like stopping after n-retries and exponential backoff that are very useful in real-world scenarios.</li>
</ul>
<hr>
<p>You'll find a lot of examples online if you went looking for a way to run a function repeatedly after a delay that goes something like this:</p>
<pre><code class="language-js">const useInterval = (fn, { delay = 5000 }) =&gt; {
  useEffect(() =&gt; {
    let id;

    if (delay === null) {
      return;
    }

    id = setInterval(fn, delay);

    return () =&gt; clearInterval(id);
  }, [delay]);
};
</code></pre>
<p>The idea is simple:</p>
<ul>
<li>you create a hook that takes the function to run and a delay time</li>
<li>and it uses a <code>useEffect</code> to setup a <code>setInterval</code> that runs the function after the delay</li>
<li>and when the component using this hook unmounts, you clear the interval</li>
</ul>
<hr>
<p>There are a bunch of problems with this approach:</p>
<ul>
<li>What if the function you want to run is async and returns (or resolves) only after a few seconds? (Ans: you'll see function stackups if your <code>delay</code> is less than the time it takes for your async function to resolve.)</li>
<li>What if you wanted to add some kind of an exponential backoff? (Ans: Not possible in the current scheme. Plain old <code>setInterval</code> is too limiting)</li>
<li>Oh what if the function throws an error?! (Ans: we could simply slap a <code>try ... catch</code> but then, it doesnt solve for advanced use-cases like retrying a few times before giving up)</li>
</ul>
<p>So let's make our <code>useInterval</code> robust by solving these problems.</p>
<p><strong>Supporting async functions</strong></p>
<p>Here's the ask: you want to run async functions repeatedly (with a delay) but you want the next-run of the function to be some seconds <em>after</em> the first run is complete. To do this, we have to <code>await</code> our function. But <code>setInterval</code> does not care for waiting - it just keeps calling whatever you give it after a delay.</p>
<p>We could use a <code>setTimeout</code> instead. Sure, the problem is it runs just once but let's see:</p>
<pre><code class="language-js">const useInterval = (fn, { delay = 5000 }) =&gt; {
  useEffect(() =&gt; {
    let id;

    if (delay === null) {
      return;
    }

    id = setTimeout(async () =&gt; {
      await fn();
    }, delay);

    return () =&gt; clearTimeout(id);
  }, [delay]);
};
</code></pre>
<p>Because all logic is inside a <code>useEffect</code>, we could simply force the <code>useEffect</code> to re-run after a delay - and that will call <code>setTimeout</code> again!</p>
<p>And <code>useEffect</code> will re-run if something changes in the dependency array. To do this, we'll just introduce a random state variable (which is just <code>Math.random()</code>):</p>
<pre><code class="language-js">const useInterval = (fn, { delay = 5000 }) =&gt; {
  let [randomN, setRandomN] = useState(Math.random());

  useEffect(() =&gt; {
    let id;

    if (delay === null) {
      return;
    }

    id = setTimeout(async () =&gt; {
      await fn();
      setRandomN(Math.random());
      clearTimeout(id);
    }, delay);

    return () =&gt; clearTimeout(id);
  }, [delay, randomN]);
};
</code></pre>
<p>What happens is this:</p>
<ul>
<li>the hook loads</li>
<li>it calls the <code>useEffect</code> function</li>
<li>which calls the <code>setTimeout</code> (if there is a valid <code>delay</code> value)</li>
<li>in the <code>setTimeout</code>, we call the function to call (and wait for it to resolve)</li>
<li>once the function is run, we clear the interval and we set a new <code>randomN</code> which triggers the <code>useEffect</code> to re-run</li>
</ul>
<hr>
<p><strong>Supporting error-retries</strong></p>
<p>But of course what's a function if it does not throw in the most unexpected way?</p>
<p>Error handling is simple: we just wrap the function call with in a <code>try ... catch</code> but what that achieves is not optimal. Why? Because if the function (for some reason) keeps throwing an error <em>all the time</em>, what's the point in calling it over and over again?</p>
<p>So we have to get the whole thing to stop if the function throws an error. We'll just be a little fancy and ask our hook to &quot;retry&quot; the function a few times before giving up.</p>
<p>That is, just two rules:</p>
<ul>
<li>don't blow up</li>
<li>try a few times</li>
</ul>
<p>To do this, we'll just do 3 things:</p>
<ul>
<li>introduce a &quot;retryCount&quot; state; except, we'll just use a ref for this because we don't want to re-render anything when it changes</li>
<li>update the retryCount when our function errors</li>
<li>and if retryCount has hit the max, we clear the timeout and stop the whole logic from running again</li>
</ul>
<pre><code class="language-js">const useInterval = (fn, { retries = 3, delay = 5000 }) =&gt; {
  let [randomN, setRandomN] = useState(Math.random());
  let retryCount = useRef(retries);

  useEffect(() =&gt; {
    let id;

    if (delay === null) {
      return;
    }

    if (retryCount.current === 0) {
      clearTimeout(id);
      return;
    }

    id = setTimeout(async () =&gt; {
      try {
        await fn();
      } catch (_) {
        retryCount.current = retryCount.current - 1;
      }
      setRandomN(Math.random());
    }, delay);

    return () =&gt; clearTimeout(id);
  }, [delay, randomN]);
};
</code></pre>
<p>There is a small problem with this logic though: our hook tracks retries but not &quot;consecutive&quot; ones. We want the hook to stop <em>only</em> if the function throws three consecutive times.</p>
<p>To do this, we'll reset the <code>retryCount</code> if the function succeeds.</p>
<pre><code class="language-js">const useInterval = (fn, { retries = 3, delay = 5000 }) =&gt; {
  let [randomN, setRandomN] = useState(Math.random());
  let retryCount = useRef(retries);

  useEffect(() =&gt; {
    let id;

    if (delay === null) {
      return;
    }

    if (retryCount.current === 0) {
      clearTimeout(id);
      return;
    }

    id = setTimeout(async () =&gt; {
      try {
        await fn();
        retryCount.current = retries;
      } catch (_) {
        retryCount.current = retryCount.current - 1;
      }
      setRandomN(Math.random());
    }, delay);

    return () =&gt; clearTimeout(id);
  }, [delay, randomN]);
};
</code></pre>
<p><strong>Adding an incremental backoff</strong></p>
<p>This is a great place to be at. But more realistically, these interval-functions need an exponential backoff so that the retries are lagged by an increasing amount of delay.</p>
<p>All we need to do is keep track of – and use – a new delay amount everytime the function runs. We can do this by introducing a new reference or variable called <code>delayAmt</code> and updating its value when the function finishes running.</p>
<pre><code class="language-js">const useInterval = (
  fn,
  { retries = 3, delay = 5000, backoffFactor = 1.2 }
) =&gt; {
  let [randomN, setRandomN] = useState(Math.random());
  let retryCount = useRef(retries);
  let delayAmt = useRef(delay);

  useEffect(() =&gt; {
    let id;

    if (delay === null) {
      return;
    }

    if (retryCount.current === 0) {
      clearTimeout(id);
      return;
    }

    id = setTimeout(async () =&gt; {
      try {
        await fn();
        retryCount.current = retries;
        delayAmt.current = delayAmt.current * backoffFactor;
      } catch (_) {
        retryCount.current = retryCount.current - 1;
      }
      setRandomN(Math.random());
    }, delayAmt.current);

    return () =&gt; clearTimeout(id);
  }, [delay, randomN]);
};
</code></pre>
<p>And that's a complete, usable useInterval hook.</p>
<p><strong>Other improvements you could try:</strong></p>
<ul>
<li>The hook should update if the function passed to it changes</li>
<li>Use this as a wrapper around popular data-fetching libraries like SWR and TanStack Query</li>
</ul>
]]></description>
<pubDate>Thu, 09 Feb 2023 12:00:00 +0530</pubDate>
</item><item>
<title>A tool-building mindset</title>
<link>https://code.druchan.com/toolbuilding-mindset</link>
<guid>https://code.druchan.com/toolbuilding-mindset</guid>
<description><![CDATA[<p>Everyone uses tools to build things.</p>
<p>Frameworks like React/Vue, libraries like Lodash/Ramda, vendor plugins etc. are tools like hammers, like bulldozers - they are ready-made, are handy to use when you build stuff (stuff in our case is web apps, or websites, or even modules that may be backend-specific).</p>
<p>Building things gets easier with tools, yes. Faster too (at least, most of the time).</p>
<p>But because these tools exist, many of us fall into a simple mindset trap: there are tools and there are apps (ie, built stuff).</p>
<p>This mindset causes us to do things that are often repetitive and time-consuming without us realizing that they are so.</p>
<p>Tools, it turns out, can be used to build bigger tools, better tools. Apps are bigger tools built from smaller tools.</p>
<p>Bigger, better tools are great because they often save time, make your code look and read better (better debugging, lesser testing!) and help you build things in a much more easier way.</p>
<p>Eg. Take the fetch API. (let's assume we want to avoid Axios for this use case).</p>
<p>The fetch API is a tool to make HTTP requests, get responses and pass it down to whatever function you have to store and process the response.</p>
<p>The level zero of using fetch in your project is using it as-is. Let's say you have a page where a component lives. The page makes a call to the API, gets the data and hands it over the component. Some pseudo code in React:</p>
<pre><code class="language-jsx">const Page = () =&gt; {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState(null);
  useEffect(() =&gt; {
    fetch(URL + &quot;/something&quot;, {
      method: &quot;get&quot;,
      headers: {
        authorization: &quot;Bearer &quot; + TOKEN,
        [&quot;content-type&quot;]: &quot;application/json&quot;,
      },
      //...other options
    })
      .then((r) =&gt; r.json())
      .then((res) =&gt; {
        setData(res);
        setLoading(false);
      });
  }, []);
  return &lt;Child data={data} loading={loading} /&gt;;
};
</code></pre>
<p>You can see where this is going to be a problem: your app is going to have so many pages, making so many calls to the API, and writing this fektch.then(r =&gt; r.json()).then(...) is going to be a chore.</p>
<p>So you build an abstraction. We will skip a few steps ahead and have ourself an async fetch wrapper - a wrapper that does three things:</p>
<ol>
<li>it automatically injects the default configuration like the method, headers and whatnot.</li>
<li>it handles an error-code response (400s, 500s) so you don't have to do <code>r.ok</code> every time to check for successful responses.</li>
<li>and it hands you data at the end if everything went well.</li>
</ol>
<p>The code for the wrapper might look something like this:</p>
<pre><code class="language-jsx">const fetchWrapper = (url, options) =&gt; {
  return fetch(url, {
    ...DEFAULT_OPTIONS,
    ...options,
  })
    .then((r) =&gt; {
      return r.ok ? Promise.resolve(r.json()) : Promise.reject(r.statusText);
    })
    .catch((e) =&gt; Promise.reject(e));
};
</code></pre>
<p>The two explicit Promise.rejects enable us to use the fetchWrapper like this:</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  fetchWrapper({
    url: &quot;/something&quot;,
  })
    .then((res) =&gt; {
      setData(res);
    })
    .catch((e) =&gt; {
      console.error(e);
      // or some toast-like notification
    })
    .then((r) =&gt; setLoading(false));
}, []);
</code></pre>
<p>Let me pause for a second here. This is not about reduction in code or something trivial like that - although this will eventually be one good reason to adopt a tool-building mindset.</p>
<p>We've built a mini tool (fetchWrapper) on top of another tool (the fetch API) which converts an unwieldy-looking, unwieldy-behaving function into a simpler one that makes it easier for us to use it.</p>
<p>But we need to go further here with the fetchWrapper. It's an okay tool, but it's not very useful yet.</p>
<p>Promises and async/await are great but here's the problem: if a promise fails (ie rejects, throws an error), you have to write the &quot;catch&quot; function to handle it. Otherwise, the app will crash and cause ugly UX issues.</p>
<p>Most developers I see have resigned to writing catch glue everywhere to handle this. Okay, but can we do better?</p>
<p>Turns out, yes.</p>
<p>Our fetchWrapper, if you think about it, either resolves into data or rejects into an error. That is, there are only two things that it &quot;returns&quot;: a data or an error.</p>
<p>Once again: the problem is we keep writing &quot;.then&quot; and &quot;.catch&quot; everytime we use the wrapper. The ideal solution is - and this sort of thing always needs a bit of imagination (sometimes bold) - that you never have to write a &quot;.then&quot; and &quot;.catch&quot;. Let's see:</p>
<p>We could go try-catch but that is not really much different now is it?:</p>
<pre><code class="language-jsx">try {
  const data = await fetchWrapper(...)
  // do something with data
} catch (e) {
  // do something with error e
}
</code></pre>
<p>It's slightly shorter but it is still repetition. Needless repetition.</p>
<p>What if the modified, better fetchWrapper could return both data and error in a single object?</p>
<pre><code class="language-jsx">const { data, error } = await fetchWrapper(...)
</code></pre>
<p>Wait, how come there is no try or catch?</p>
<p>Because fetchWrapper takes care of &quot;catching&quot; errors for us and returns it in a &quot;safe&quot; way.</p>
<p>Actually, if we implement fetchWrapper this way - where it never throws or rejects but returns an error as a simple object key - we eliminate a big class of problems: ie, your app crashing because something at the API failed for some weird reason.</p>
<p>This is not relevant for this talk but here's how the code might look. Again, this is just pseudo code:</p>
<pre><code class="language-jsx">const fetchWrapper = (url, options) =&gt; {
  return fetch(url, {
    ...DEFAULT_OPTIONS,
    ...options,
  })
    .then((r) =&gt; {
      return r.ok
        ? Promise.resolve({
            data: r.json(),
            error: null,
          })
        : Promise.resolve({
            data: null,
            error: r.statusText,
          });
    })
    .catch((e) =&gt;
      Promise.resolve({
        data: null,
        error: e.toString(),
      })
    );
};
</code></pre>
<p>The fetchWrapper is still a Promise but it will never &quot;reject&quot;. In other words, you will never have to write a '.catch' for it and if you use async/await sugar, you will never have to write '.then' either. Both data and error come in the result value and if there is data, error is null and if there is error, data is null.</p>
<p>In your use of the wrapper, you'll simply do this:</p>
<pre><code class="language-jsx">const { data, error } = await fetchWrapper(...)
if (data) {
  setData(data)
}
if (error) {
  setError(error)
}
setLoading(false)
</code></pre>
<p>The bigger and better tool-building mindset is not just about saving time or reducing lines of code. It is also about paving way for a better API for your own internal use. Like the poor fetch becomes a better fetch and you destroy the chance of errors crashing your app because of the API-calling layer.</p>
<p>Let's take another example. Most components that receive data (esp from an API call) will need to have three states:</p>
<ol>
<li>the loading state</li>
<li>the data state</li>
<li>the error state where the expected data couldn't come through We will ignore the empty state. Let's assume it's part of the data state.</li>
</ol>
<p>Most of us have written this kind of stuff in every component:</p>
<pre><code class="language-jsx">const Child = (props) =&gt; {
  if (props.loading) return &lt;Loader /&gt;;
  if (props.error) return &lt;div&gt;{props.error}&lt;/div&gt;;
  if (props.data) return &lt;div&gt; ... &lt;/div&gt;;
  return &lt;&gt;&lt;/&gt;;
};
</code></pre>
<p>Imagine writing something similar for every single component in your app.</p>
<p>It doesn't look daunting at all because we're kind of used to doing it. But at the end of the app building exercise, look back at the number of components you wrote this boilerplate and you'll realize it's a horrendous amount of time spent doing that.</p>
<p>If we apply the tool building mindset to this, we will think of a component that takes care of rendering a loader or showing an error for all components without us having to do it manually every time.</p>
<p>Here's a basic implementation in React:</p>
<pre><code class="language-jsx">const DataWrapper = (props) =&gt; {
  if (props.loading) return &lt;Loading /&gt;;
  if (props.error) return &lt;div&gt; {error} &lt;/div&gt;;
  return &lt;&gt;{props.children}&lt;/&gt;;
};
</code></pre>
<p>And we'd use it like so:</p>
<pre><code class="language-jsx">&lt;DataWrapper loading={loading} error={error}&gt;
  &lt;Child data={data} /&gt;
&lt;/DataWrapper&gt;
</code></pre>
<p>In Vue, for example, this use case will translate to slots.</p>
<p>With this, all your components have to be written as if they will only have data (or data could be null or something). You can stop wiring the loading and error states throughout the codebase.</p>
<p>Of course you might want to make DataWrapper more fine-grained with more options for things like a different-styled loader or different-styled error render but they are trivial things to do once you build the base.</p>
<p>The point, of course, is this: when we walk into our workday, we already have tools given to us, selected by us, tools that we use often, tools we've gotten fond of, tools we swear by.</p>
<p>They will help you build your app, sure. But they are not built for your app - they are built for all apps. Which means they are not crafted specifically to make building your app easy, fast and seamless.</p>
<p>It is up to us and up to the app we're working on to build bigger tools using the tools we're given to make the whole experience of building simpler, better and smoother.</p>
]]></description>
<pubDate>Sat, 17 Dec 2022 12:00:00 +0530</pubDate>
</item><item>
<title>'Should I Learn Monads?'</title>
<link>https://code.druchan.com/should-i-learn-monads</link>
<guid>https://code.druchan.com/should-i-learn-monads</guid>
<description><![CDATA[<p>Someone asked about this <a href="https://www.reddit.com/r/functionalprogramming/comments/r9r7gf/should_i_learn_about_monads/">on reddit</a> today and I wanted to think about this a bit.</p>
<ul>
<li>This is written (or thought) from the perspective of a JS developer who discovered functional programming as recently as 2019 and has used monads.</li>
<li>This is not a tutorial on monads, by the way.</li>
</ul>
<p><strong>Propriety</strong></p>
<p>I still do not profess to know monads ... but I consider that I do understand core idea of functional monads. FWIW, I've implemented monads from scratch in Purescript through <a href="https://blog.curlyfri.es/monad-challenges-purescript/">this fascinating resource</a>.</p>
<p><strong>Has monads changed the way I write JS code?</strong></p>
<p>Absolutely.</p>
<p>It's quite normal for JS developers to be okay with doing <code>possibleObject.someProperty</code> when <code>possibleObject</code> is not guaranteed to be an object in the first place. That is, it could be &quot;null&quot; or &quot;undefined&quot;. Learning monads and using them (the <code>Maybe</code> monad in this case) has helped me be aware of and mitigate this risk (of property access in vanilla JS). Now, I use <code>get</code> from <a href="https://lodash.com">Lodash</a> or the fancier <code>S.prop</code> from <a href="https://sanctuary.js.org/">Sanctuary</a> depending on the project.</p>
<p>Functions that could throw errors (known and unknown) will need to be handled with <code>try catch</code> blocks. The <code>Either</code> monad has helped me not only be more conscious about such functions when I use them but also model my data structures and pipeline functions better to handle this easily. Think of this: instead of <code>try catch</code> all over the place, I wrap functions in a way that they will return a tuple of <code>[error, data]</code> and I just need to check for either to be <code>null</code> to know if the function worked or failed. No <code>try catch</code> blocks all over the place.</p>
<p>Monad's <code>bind</code> and <code>map</code> have helped me understand how useful such functions are when dealing with pipelines and data transformation (which is about 80% of my work). Maybe they are only useful when you write code in a functional-style but the utility is enormous nevertheless.</p>
<p>Learning monads has also helped me quickly identify certain patterns (for example, think of running an array of promises parallelly and then processing the result. In JS, this is <code>[Promise&lt;value1&gt;, Promise&lt;value2&gt;, Promise&lt;value3&gt;]</code> but using this data as-is is a messy business. But if you convert this into a <code>Promise&lt;[value1, value2, value3]&gt;</code>, suddenly, you only have to unwrap a single promise (via <code>await</code>) to get all the values and have fun using simple, synchronous array functions. This idea is called <code>sequence</code> and within the FP-world, most libraries provide this function).</p>
<p><strong>Learning monads</strong></p>
<p>While this is a completely subjective feeling, I think the best way to learn about monads would be to actually build them from scratch.</p>
<p>The monad challenges were hard. I spent days solving some of them and had to rope in some help from the FP community.</p>
<p>But in the end, while I came out of it battered, some of the core ideas of monads (and why we have them in the first place) got ingrained in me. And they've made writing and approaching problems (to be solved through code) much easier.</p>
<p><strong>Also</strong>:</p>
<ul>
<li><a href="/safe-vs-unsafe-js">Safe vs Unsafe Javascript</a></li>
</ul>
]]></description>
<pubDate>Mon, 06 Dec 2021 12:00:00 +0530</pubDate>
</item><item>
<title>Safe vs Unsafe JavaScript</title>
<link>https://code.druchan.com/safe-vs-unsafe-js</link>
<guid>https://code.druchan.com/safe-vs-unsafe-js</guid>
<description><![CDATA[<blockquote>
<p>TLDR: Try to catch errors early, convert errors into &quot;data&quot; that can safely be passed around without the fear of your app crashing. JavaScript does not provide built-in mechanisms like <code>Either</code> to do this but you can build a trivial one yourself. Make programs safe.</p>
</blockquote>
<p>The other day a colleague and I got into a conundrum involving JavaScript Promises, unhandled exceptions and who ultimately should own the responsibility of handling thrown errors and rejected promises.</p>
<p>Here's some setup.</p>
<p>Your frontend architecture has three distinct layers that work like a pipeline:</p>
<ul>
<li>an API handler which is more or less a wrapper for a Promise-based API library like <a href="https://github.com/axios/axios">Axios</a>/<a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch</a> which makes the calls to the backend service</li>
<li>a caching layer which exposes data and functions to query and mutate the data (mutations ultimately get passed over to the API layer)</li>
<li>and the app's rendering layer which consists of pages and components</li>
</ul>
<p>One of the first places your app can &quot;throw&quot; an error is the API layer. For example, all HTTP responses that are in the 4xx or 5xx status zones are technically errors for Axios.</p>
<p>Since you export these helpers out to the caching layer, you have the option of either handling the error at the API layer and passing a &quot;safe&quot; data-only artefact to the subsequent layers of your pipeline, or letting the subsequent layers handle the error the way they see it fit.</p>
<p>Now this is a problem. Why? Because there is no standard operating procedure (enforced either by the language's built-in paradigms or by coding discipline + style-guide), any opinion on who should &quot;handle&quot; the error finds merit. Should it be the API layer itself? Or should it be the component/page that actually triggers the pipeline?</p>
<p>In our case, we also had a constraint: our caching layer needed functions that will &quot;throw&quot; in case of errors (in order for it to run some side-effects on error). One of the libraries being used deep inside components would also need promises that can reject/throw. So, since we were passing around promises across the pipeline, the argument went, the final consumer can do a simple <code>.catch</code> (in our case with a <code>noop</code> for the <code>catch</code>) and none will be the wiser. Long story short, we have a codebase where unhandled promise rejections are being piped across the app.</p>
<p>I think there's some sort of a Sapir-Whorf equivalent to programming languages as well. At a different time, I'd not think this approach is inherently risky and wrong because JavaScript does not provide built-in mechanisms to do two things that completely alter the way we think about unhandled exceptions: 1) a way to safely wrap errors so they wont crash an app and 2) a way to handle such wrapped errors and pipe them across so that they can be handled just at the level they need to be - again, without crashing the app.</p>
<p>My first tryst with functional programming came when I started reading about this frontend language that compiled to JS, called <a href="https://elm-lang.org">Elm</a> and one of the USPs that it tooted often was &quot;No runtime errors!&quot; (Given how notorious JS is for runtime type errors and <code>undefined</code>s, this is a fantastic marketing tag for Elm).</p>
<p>In Elm, I found the ideas of what's called the <code>Either</code> (or <code>Result</code>) datatype that lets you - very safely - wrap the output of a function (like an Axios promise) even if it throws an error so that your app can continue to work without crashing. You can then inspect what's inside the <code>Either</code> - if it's a <code>Left</code>, then it's an error and if it's a <code>Right</code> then you have successful data. (And this is a concept available in all typed languages that offer some category theory sprinkling)</p>
<p>Of course this is not enough. You need mechanisms in the language that let you do that <em>inspection</em> easily. That part is provided by <a href="https://www.haskell.org/tutorial/patterns.html">pattern matching / case expressions</a> in many functional languages. I think our approach to solving these classes of problems is influenced by the languages we &quot;speak&quot;. Knowing and using ideas such as <code>Either/Result</code> alters the way we look at such problems.</p>
<p>At Algoshelf, when I wrote the API layer for a VueJS frontend, one of the first things I did was to turn the response from Axios into a tuple of <code>(data, error)</code> (of course, represented in JavaScript as a plain array of length 2). Every function down the pipeline, then, had merely to do a case expression (<code>if</code>). Luckily, the whole architecture was designed so we didn't have to write boilerplate code for everytime we were using those functions. And the component design was such that the wrapping component would handle it for us.</p>
<p>The general principle here is that &quot;data&quot; is a safer thing to carry around and pass in your application than &quot;error&quot; (especially the kinds that need a &quot;catch&quot; mechanism). Instead, adopting a mechanism where errors are turned into data at the first instance gives us the ability to have a consistent, uniform and a safe way to inspect the contents of the data and decide if it's a successful response or an error one.</p>
<p>JavaScript does not provide such mechanisms out of the box but that does not mean you can't build one yourself. Libraries like Sanctuary/Folktale make it easier to get such paradigms imported into your JavaScript code.</p>
]]></description>
<pubDate>Sun, 20 Dec 2020 12:00:00 +0530</pubDate>
</item><item>
<title>Coder’s block</title>
<link>https://code.druchan.com/coders-block</link>
<guid>https://code.druchan.com/coders-block</guid>
<description><![CDATA[<p>In a recent conversation, I made a comment that came out of me unconsciously. Or so I’d like to believe because it almost certainly was not a well-thought-out remark.</p>
<p>I said, “I dont want to think when I’m writing code”. It has been more than a day since I said it and I’ve been thinking about this remark all throughout. Why did I say it? What did I really mean? etc.</p>
<p>When I think as I write code, the whole process of writing some logic and making it work feels tedious. Almost like drudgery. Things go wrong in oh-so-many ways and it’s more like fighting against a machine with an almost unconquerable will than writing logical instructions to a machine that is ready to do your bidding.</p>
<p>The opposite of this is when I <em>know</em> what needs to be written and I just write it. Sure, it breaks in a few ways but these are tiny annoyances that are remedied almost immediately - or with a slight pause - and then the almost-smooth-sailing resumes.</p>
<p>So the trick (to happier code-writing experiences) seems to be to <em>know</em> in advance what’s going to be written. This completely avoids the “thinking while writing code” problem and - perhaps, as a result of that - gives a great code-writing experience.</p>
<p>But of course, I’m no genius so I can’t really know what I’m going to write. There’s always a trial and error even when I’m fairly confident of the kind of logic I’m going to implement for a specific feature/problem. But fighting through that trial and error is easier than doing that <em>thinking</em> while also writing code.</p>
<p>I didn’t know it at the time and only realized it now while thinking about this that I had the same problem with writing (in general). If I had spent my days marinating in thoughts about something and, more importantly, forming the structure of the thing I’m thinking about, my writing on that would almost <em>flow</em>. If instead I fired up the editor with just the crux of a topic, I could go weeks with nothing to show except feeling battered. The coder’s block is the same as the writer’s block.</p>
<p>These lessons always sound blatantly obvious in hindsight. These are lessons we’ve read about, heard about, seen about in various forms. Visualizing the thing you are about to produce gives you a fair upper-hand in skirting the problem of a writer’s, coder’s and creator’s block.</p>
<p>I am pretty certain I’d forget this lesson four weeks down the line and stare at stray pieces of code I’m completely unhappy about unless I re-read this multiple times a month. But there is one interesting meta-application here: I spent a better part of the day thinking about this whole write-up as well and that has paid dividends.</p>
]]></description>
<pubDate>Thu, 17 Dec 2020 12:00:00 +0530</pubDate>
</item><item>
<title>To scale productivity in code</title>
<link>https://code.druchan.com/to-scale-productivity-in-code</link>
<guid>https://code.druchan.com/to-scale-productivity-in-code</guid>
<description><![CDATA[<p>A startup founder - let’s call him Nate - I worked with had this interesting philosophy of optimization where “automation” was a key to doing things efficiently. While PG wrote “do things that don’t scale”, Nate swung in the other direction – he would pick a thing almost only if it was scalable. And “can it be automated?” was one of the important yardsticks to see if something was scalable and, therefore, worth his time.</p>
<p>I think somewhere this notion is prevalent in many people. A co-founder from another startup - let’s call him Abe - exhibited this in a different way. While Nate’s automation yardstick was applied to everything in the business world (which is to say it encompassed everything from marketing to sales to engineering), Abe’s was more pronounced in the engineering department. Perhaps this is because he was more of an engineer than an overall-product person in the scope/context I knew him.</p>
<p>In building the frontend, Abe came up with this idea that it should be easy to “compose” not just the components we write but also an entire app with specific features disabled. Not only was it helpful in the business sense (eg feature-flagged product) but also helpful in isolated tests of complete business modules. We were building an application (think super-app) with a bunch of sub-apps in it and his idea was for anyone with bare mininum tech chops to be able to build the modules together.</p>
<p>That idea morphed into a philosophy for the app we built (at least to a decent degree, I think). So now I was trying not only to write a build system that would allow someone to selectively build the modules that would render in the final app but also enable people to write components that plug into the database somewhat more easily than usual. As a quick example, I wrote an entire layer that abstracted the API interactions (think Vuex Actions) and store access to a level where a simple wrapping component was all you needed to get data (along with loading, error states plus HTTP POST/PUT/DELETE handlers) to all the APIs in our app.</p>
<p>At the time of writing such layers of abstraction that would automate a lot of things for the developer, I was not aware of such patterns pre-existing in other frameworks (React/Apollo for instance). Now, I see that pattern in a lot of places. The crowning glory however – not to sound boastful but still – was the fact that I was able to write something from scratch which ended up saving me a lot of time.</p>
<p>The seed for that endeavour, which I am sure I wouldn’t have undertaken had it not been put on my desk as a requirement from who could often sound like a madman, came from Abe’s incessant need to simplify writing code by using generators, decorators, abstractions and what other ideas have you to write lesser code. In that pursuit, you usually end up writing a hell lot of code in the first few weeks of your system getting into shape and then it pays rich dividends.</p>
<p>If some engineer were to say, “my goal is not only to build a great product for the organization I work for but also to make my life easier and write less code”, most people would probably balk at the idea and not hire them. Yet, that is precisely the kind of ideas Abe has/had in his mind and that is what led us to build a frontend that had such levels of abstraction. A culture of “how can we create a tool here that will help us make building things easier?” should be in the minds of every engineer. While not all of us can create rich frameworks like React or Vue, we certainly can create tools with existing libraries or prior art that would help us in our everyday lives.</p>
]]></description>
<pubDate>Sat, 12 Dec 2020 12:00:00 +0530</pubDate>
</item><item>
<title>Everything hard is easy again</title>
<link>https://code.druchan.com/everything-hard-is-easy-again</link>
<guid>https://code.druchan.com/everything-hard-is-easy-again</guid>
<description><![CDATA[<p>When I read <a href="https://frankchimero.com/writing/everything-easy-is-hard-again/">Frank Chimero’s piece</a> on the state of web dev, I was ecstatic that someone with so much clout and know-how wrote exactly about the frustrations I was feeling - but more in the sense of not being able to make sense of all this <code>npm install</code> shit that seems to be the start of every tutorial these days than as someone working (sometimes) as a web dev.</p>
<p>For many months, I had been avoiding this npm and webpack route like the plague. Until I hit a point where I <em>had</em> to work with <code>vue-cli</code> for a project (subsequently couple more projects). I still have some qualms about this new web-dev workflow that we’ve got ourselves into but I had the luxury of some quiet evenings to ponder over my initial — but long — aversion to the current state of web-dev workflows and webpacks and whatnots.</p>
<p>Turns out, in my specific case, there were two issues.</p>
<p>One, I like to know “why” and “why not something else” when someone tells me to do something. Without a convincing “why” and “why not”, I cannot wrap my head around the rest of the instruction set. Since 2015 or thereabouts, if you take a look at tutorials on web dev, almost everything is exactly like how Frank Chimero describes it:</p>
<blockquote>
<p>simply npm your webpack via grunt with vue babel or bower to react asdfjkl;lkdhgxdlciuhw</p>
</blockquote>
<p>Two, I was on a hiatus and that meant I missed a big chunk of historic context on how we got here.</p>
<p>The first issue is a constant and is more of a good feature than an issue so that stays. (I still get pissed off at tutorials that aim to do the simplest of things but will require you to <code>npm install</code> half-a-dozen packages. If fucking packages are getting things done, why do you even bother to write a tutorial, ya nincompoop)</p>
<p>The second issue is something resolvable. So on one of the quiet evenings I traced the history of web dev workflows.</p>
<p>And that’s when everything cleared and my aversion for <code>npm</code>-style workflow melted away.</p>
<p>It all starts with our penchant for economy of effort.</p>
<ul>
<li>Things like package managers came to be because we started including a lot of js libraries in our projects. DRY took a stronghold (but note how the best creators are often the ones who flout the DRY rule and always build things their own way from scratch?). Handling these libraries became a chore when you had to upgrade them.</li>
<li>Things like bundling came to be because the benefit of minification was for all to see.</li>
<li>Things like transpilers and compilers came to be because we wanted to go with Python-like simplicity in CSS and OOP-like functionality in JS.</li>
<li>Things like starter packs are fairly simple to trace: we had ‘alias'es in our .bashrc file to create a project file, touch index.html and mkdir some folders like 'js’ and 'css’ and so on.</li>
<li>And finally, of course, we have things like hot reloading and live reloading and libraries that handle these - and these came to be out of this whole packaging, bundling, compiling consortium.</li>
</ul>
<p>The real hard thing was a year or so ago when each existed independently. I know one project where the dev was using “bower” to manage packages, using “grunt” to serve and build the app, then there was node too to wrap all this. Imagine having to remember the commands for each (the lazy programmer would say “but there are only a handful of commands that you use constantly so it’s no big deal”).</p>
<p>What happend with this <code>npm</code> craze is that we managed to bundle all of these things - dependencies (a.k.a package managers), compilers/transpilers, local server setup, hot/live reloading into one nifty command-line tool.</p>
<p>And so, technically, everything hard has become somewhat easy again.</p>
<p>It still doesn’t justify the stupid tutorials that start with “install these 73 packages first and then we’ll start connecting with that API to fetch data”.</p>
]]></description>
<pubDate>Wed, 16 May 2018 12:00:00 +0530</pubDate>
</item><item>
<title>Half-cooked Async/Await</title>
<link>https://code.druchan.com/half-cooked-asyncawait</link>
<guid>https://code.druchan.com/half-cooked-asyncawait</guid>
<description><![CDATA[<p><strong>Update (2019)</strong>: I've changed my mind about this and prefer <code>async/await</code> over the callback-hell.</p>
<hr>
<p>I wrote <a href="http://druchan.com/gen_id">a library that generates unique IDs</a> of any reasonable character length.</p>
<p>Yes, there are possibly thousands like these. But this is a good, basic learning experience and that’s the only reason I did this.</p>
<p>But turns out I could then expand this into something a bit more.</p>
<p>And so it turned out to be a lesson in the new Async/Await thing in JS.</p>
<p>The <code>generateID(options)</code> function is written as a promise.</p>
<p>And the <code>Generate Id</code> button/link on the demo page uses the async/await method to print the result on the page.</p>
<p>Exactly why should <code>generateID()</code> be a promise? Why not a simple, straightforward function?</p>
<p>In the real world, <code>generateID()</code> is useful for generating unique IDs. Sort of like primary keys for rows of data (or documents, if you come from MongoDB).</p>
<p>One use-case would be to ensure that the generated ID is truly ‘unique’. That means cross-checking the generated ID with the existing ones and confirming that there’s no duplicate.</p>
<p>As this process takes time, I converted the <code>generateID(options)</code> function into a promise.</p>
<p>There may be other use-cases. I’m not aware of them, I can’t think of any other at the moment of writing this. But the one above is quite important when people use libraries to generate unique IDs.</p>
<p>So, in essence, this library is instantly extensible. I put in a dummy/silly function in the library as an example. <code>generateID(options)</code> will check if the generated ID contains the letter 'o’ … if not found, it will throw an error. (You can tweak this function to do something valuable instead: like check for uniqueness of the generated ID).</p>
<p>Here’s where the stupidity of Async/Await shows up - a.k.a it’s still immature.</p>
<p>Typically, I’d use a simple <code>generateID(options).then().catch()</code> kind of a code on the front-end. You would too. But there’s this async/await fad going about. So let’s try that.</p>
<p>When you click “Generate ID”:</p>
<p>I do this:</p>
<p><img src="https://64.media.tumblr.com/bb99b5adf8ba6a66295c3d95d4352d30/tumblr_inline_p7q6j3VMJf1qbg0pd_540.png" alt=""></p>
<p>But <code>await</code> cannot be used like that. It <em>has</em> to be inside an <code>async</code> function. Er… that seems stupid.</p>
<p>So we have to rewrite it.</p>
<p><img src="https://64.media.tumblr.com/d54d4ee3c84fe86d16fa671000d83c9e/tumblr_inline_p7q6huq2jT1qbg0pd_540.png" alt=""></p>
<p>Okay this worked. But hold up. What if there was an error in the <code>generateID()</code> function itself? Like, it threw a <code>reject()</code> instead of a <code>resolve()</code>?</p>
<p>There’s no way async/await can handle errors. You have to manually try and catch errors. Get it?</p>
<p><img src="https://64.media.tumblr.com/6597d56323598b1d1b5d3fdbde37b410/tumblr_inline_p7q6mbCnzu1qbg0pd_540.png" alt=""></p>
<p>Compare this with how you’d typically handle a promise.</p>
<p><img src="https://64.media.tumblr.com/bf1f3df04447904ad695d78b810261ba/tumblr_inline_p7q6uucVbi1qbg0pd_540.png" alt=""></p>
<p>I still think the chaining is better than this half-cooked async/await thing we’re being sold.</p>
]]></description>
<pubDate>Wed, 25 Apr 2018 12:00:00 +0530</pubDate>
</item> </channel>
</rss>