<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dmitrii Kovanikov</title>
    <description>The latest articles on DEV Community by Dmitrii Kovanikov (@chshersh).</description>
    <link>https://dev.to/chshersh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1040562%2Fbdbae340-9482-440a-a3da-d26f503db663.JPEG</url>
      <title>DEV Community: Dmitrii Kovanikov</title>
      <link>https://dev.to/chshersh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chshersh"/>
    <language>en</language>
    <item>
      <title>Pragmatic Category Theory | Part 2: Composing Semigroups</title>
      <dc:creator>Dmitrii Kovanikov</dc:creator>
      <pubDate>Mon, 19 Aug 2024 07:23:43 +0000</pubDate>
      <link>https://dev.to/chshersh/pragmatic-category-theory-part-2-composing-semigroups-87</link>
      <guid>https://dev.to/chshersh/pragmatic-category-theory-part-2-composing-semigroups-87</guid>
      <description>&lt;p&gt;This article was permanently moved to my personal website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chshersh.com/blog/2024-08-19-pragmatic-category-theory-part-02.html" rel="noopener noreferrer"&gt;chshersh.com :: Pragmatic Category Theory | Part 2: Composing Semigroups&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ocaml</category>
      <category>categorytheory</category>
      <category>math</category>
      <category>pattern</category>
    </item>
    <item>
      <title>Pragmatic Category Theory | Part 1: Semigroup Intro</title>
      <dc:creator>Dmitrii Kovanikov</dc:creator>
      <pubDate>Tue, 30 Jul 2024 18:26:33 +0000</pubDate>
      <link>https://dev.to/chshersh/pragmatic-category-theory-part-1-semigroup-intro-1ign</link>
      <guid>https://dev.to/chshersh/pragmatic-category-theory-part-1-semigroup-intro-1ign</guid>
      <description>&lt;p&gt;This article was permanently moved to my personal website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chshersh.com/blog/2024-07-30-pragmatic-category-theory-part-01.html" rel="noopener noreferrer"&gt;chshersh.com :: Pragmatic Category Theory | Part 1: Semigroup Intro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ocaml</category>
      <category>categorytheory</category>
      <category>math</category>
      <category>pattern</category>
    </item>
    <item>
      <title>7 OCaml Gotchas</title>
      <dc:creator>Dmitrii Kovanikov</dc:creator>
      <pubDate>Mon, 20 May 2024 08:18:24 +0000</pubDate>
      <link>https://dev.to/chshersh/7-ocaml-gotchas-207e</link>
      <guid>https://dev.to/chshersh/7-ocaml-gotchas-207e</guid>
      <description>&lt;p&gt;This article was permanently moved to my personal website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chshersh.com/blog/2024-05-20-7-ocaml-gotchas.html" rel="noopener noreferrer"&gt;chshersh.com :: 7 OCaml Gotchas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ocaml</category>
      <category>functional</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Learn Lambda Calculus in 10 minutes with OCaml</title>
      <dc:creator>Dmitrii Kovanikov</dc:creator>
      <pubDate>Mon, 05 Feb 2024 08:23:22 +0000</pubDate>
      <link>https://dev.to/chshersh/learn-lambda-calculus-in-10-minutes-with-ocaml-56ba</link>
      <guid>https://dev.to/chshersh/learn-lambda-calculus-in-10-minutes-with-ocaml-56ba</guid>
      <description>&lt;p&gt;This article was permanently moved to my personal website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chshersh.com/blog/2024-02-05-learn-lambda-calculus-in-10-minutes-with-ocaml.html" rel="noopener noreferrer"&gt;chshersh.com :: Learn Lambda Calculus in 10 minutes with OCaml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ocaml</category>
      <category>programming</category>
      <category>functional</category>
    </item>
    <item>
      <title>8 months of OCaml after 8 years of Haskell in production</title>
      <dc:creator>Dmitrii Kovanikov</dc:creator>
      <pubDate>Sat, 16 Dec 2023 10:15:15 +0000</pubDate>
      <link>https://dev.to/chshersh/8-months-of-ocaml-after-8-years-of-haskell-in-production-h96</link>
      <guid>https://dev.to/chshersh/8-months-of-ocaml-after-8-years-of-haskell-in-production-h96</guid>
      <description>&lt;p&gt;This article was permanently moved to my personal website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chshersh.com/blog/2023-12-16-8-months-of-ocaml-after-8-years-of-haskell.html" rel="noopener noreferrer"&gt;chshersh.com :: 8 months of OCaml after 8 years of Haskell in production&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ocaml</category>
      <category>haskell</category>
      <category>functional</category>
      <category>programming</category>
    </item>
    <item>
      <title>Haskell/GHC refuses to compile ugly code</title>
      <dc:creator>Dmitrii Kovanikov</dc:creator>
      <pubDate>Fri, 14 Apr 2023 07:58:38 +0000</pubDate>
      <link>https://dev.to/chshersh/haskellghc-refuses-to-compile-ugly-code-3l75</link>
      <guid>https://dev.to/chshersh/haskellghc-refuses-to-compile-ugly-code-3l75</guid>
      <description>&lt;p&gt;Recently, I experienced an unusual challenge when upgrading a 60K LOC Haskell project from GHC version 9.0 to 9.2, and I'd like to share my investigation journey and how I eventually fixed the problem.&lt;/p&gt;

&lt;p&gt;Grab a cup of tea, and let's begin the journey!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Don't write ugly code, otherwise, it won't compile later 🙅‍♀️&lt;/p&gt;

&lt;h2&gt;
  
  
  🐞 The Problem
&lt;/h2&gt;

&lt;p&gt;The compilation of a single 200-lines testing file required too much memory, and the build step was constantly failing on CI due to the Out-Of-Memory (OOM) error.&lt;/p&gt;

&lt;p&gt;The description of the issue is straightforward but the reasons are mysterious.&lt;/p&gt;

&lt;p&gt;What made the situation worse, is that my local laptop has 48GB RAM while the CI runner apparently is a teapot. So I wasn't able to reproduce this problem locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  🕵🏻‍♀️ Investigation
&lt;/h2&gt;

&lt;p&gt;GHC is the Haskell compiler. It's an extremely powerful tool. It does lots of stuff, and that's why it usually requires more time and memory than you may think it should.&lt;/p&gt;

&lt;p&gt;However, sometimes it takes unexpectedly more resources. This may happen for various reasons, including but not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A big record type (see relevant &lt;a href="https://well-typed.com/blog/2021/08/large-records/"&gt;blog post&lt;/a&gt; for details)&lt;/li&gt;
&lt;li&gt;A big derived instance (e.g. &lt;code&gt;Generic&lt;/code&gt; or &lt;code&gt;Read&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Lots of instances&lt;/li&gt;
&lt;li&gt;A really big module (e.g. 20K Loc &lt;code&gt;Types&lt;/code&gt; module)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⛰️ The gist: usually, something is &lt;strong&gt;really big&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my case, nothing really seemed particularly alarming:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;202-lines module with 69 lines of imports&lt;/li&gt;
&lt;li&gt;A single testing function comprising 124 lines&lt;/li&gt;
&lt;li&gt;No new types defined&lt;/li&gt;
&lt;li&gt;No instances derived&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may develop an intuition for troubleshooting similar problems and be able to make pretty good educated guesses after many years with Haskell. But the most robust way to see what's actually going on is to check Core - the intermediate representation of Haskell AST after parsing, desugaring and optimising.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Core
&lt;/h2&gt;

&lt;p&gt;You can view Core of a particular module by adding the following line to the top of the corresponding Haskell file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="cp"&gt;{-# OPTIONS_GHC -ddump-simpl #-}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, Core is pretty ugly and unreadable. So you may need to add a few extra options to actually be able to read it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="cp"&gt;{-# OPTIONS_GHC 
      -ddump-simpl
      -dsuppress-idinfo
      -dsuppress-coercions
      -dsuppress-type-applications
      -dsuppress-uniques
      -dsuppress-module-prefixes
#-}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Core looks like really verbose and explicit Haskell with lots of type annotations and argument passing but at least with the above options you're able to recognize some parts of it.&lt;/p&gt;

&lt;p&gt;Here is an example from the problematic module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- RHS size: {terms: 46, types: 68, coercions: 18, joins: 0/0}&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;wcheckUnblockedEmail&lt;/span&gt;
  &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;LogFunc&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="kt"&gt;Connection&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Connection&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Vector&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;LocalPool&lt;/span&gt; &lt;span class="kt"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="kt"&gt;RealWorld&lt;/span&gt;
     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="kt"&gt;RealWorld&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;wcheckUnblockedEmail&lt;/span&gt;
  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ww&lt;/span&gt;
         &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;LogFunc&lt;/span&gt;
         &lt;span class="kt"&gt;Unf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kt"&gt;OtherCon&lt;/span&gt; &lt;span class="kt"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ww1&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="kt"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ww2&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Connection&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ww3&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ww4&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ww5&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Vector&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;LocalPool&lt;/span&gt; &lt;span class="kt"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;State&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="kt"&gt;RealWorld&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;wrunDBWithTransactionFunction&lt;/span&gt;
             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withTransaction1&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Co&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lvl15&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Co&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="n"&gt;ww&lt;/span&gt;
             &lt;span class="n"&gt;ww1&lt;/span&gt;
             &lt;span class="n"&gt;ww2&lt;/span&gt;
             &lt;span class="n"&gt;ww3&lt;/span&gt;
             &lt;span class="n"&gt;ww4&lt;/span&gt;
             &lt;span class="n"&gt;ww5&lt;/span&gt;
             &lt;span class="n"&gt;w&lt;/span&gt;
      &lt;span class="kr"&gt;of&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ipv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ipv1&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ipv1&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;wexpectationFailure&lt;/span&gt; &lt;span class="n"&gt;lvl5&lt;/span&gt; &lt;span class="n"&gt;ipv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;queuedTask&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;queuedTask&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;QueuedTask&lt;/span&gt; &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="n"&gt;ds4&lt;/span&gt; &lt;span class="n"&gt;dt1&lt;/span&gt; &lt;span class="n"&gt;ds5&lt;/span&gt; &lt;span class="n"&gt;ds6&lt;/span&gt; &lt;span class="n"&gt;ds7&lt;/span&gt; &lt;span class="n"&gt;ds8&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ds5&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;MagicLinkEmail&lt;/span&gt; &lt;span class="n"&gt;ds2&lt;/span&gt; &lt;span class="n"&gt;ds3&lt;/span&gt; &lt;span class="n"&gt;ds10&lt;/span&gt; &lt;span class="n"&gt;ds11&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ds11&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt; &lt;span class="n"&gt;lwild&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;__DEFAULT&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
              &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="n"&gt;dataToTag&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;lwild&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;__DEFAULT&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lvl6&lt;/span&gt; &lt;span class="n"&gt;ipv&lt;/span&gt; &lt;span class="n"&gt;lwild&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="kt"&gt;MagicLinkEmailModeLogin&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;ipv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to diagnose Haskell problems with Core and understand what's actually going on, you typically spend 5 years doing GHC development or a PhD in Haskell.&lt;/p&gt;

&lt;p&gt;In my case, I was lucky enough to come across a similar problem earlier which turns out to be a GHC bug:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.haskell.org/ghc/ghc/-/issues/22824"&gt;https://gitlab.haskell.org/ghc/ghc/-/issues/22824&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem was that GHC generates a lot of Core due to exponential inlining (and maybe something else). Like, really &lt;strong&gt;A LOT&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Core has 3300 LOC while the original source code is 200 LOC which is 16.5x blow up (sick !!!).&lt;/p&gt;

&lt;p&gt;As you noticed earlier, Core is really verbose. But this huge growth is too much.&lt;/p&gt;

&lt;h3&gt;
  
  
  💻 Code
&lt;/h3&gt;

&lt;p&gt;Now, what about the code? The module has only a single test for the email auth scenario. The test is 130 lines long.&lt;/p&gt;

&lt;p&gt;But if we look in the middle of the generated Core, we notice the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's really nested&lt;/li&gt;
&lt;li&gt;There's a pattern-matching in almost every function call and this introduces more nestedness and more matching&lt;/li&gt;
&lt;li&gt;Records in Haskell are types with multiple fields in disguise and pattern matching on them every time results in lots of extra code&lt;/li&gt;
&lt;li&gt;GHC also inlines library functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here is a part of Core in question:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq181w4m8u8it945bhzw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq181w4m8u8it945bhzw4.png" alt="Middle Of Core" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After we see the problem, we need to fix it.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔨 Fix
&lt;/h3&gt;

&lt;p&gt;A quick fix would be to tune some GHC flags to prevent the compiler from doing what it's doing.&lt;/p&gt;

&lt;p&gt;Unfortunately, after some experimentation, it turned out to be impossible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;☝️ You can't change GHC! You need to accept it for what it really is.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I was left with only one solution: refactor code ⚒️&lt;/p&gt;

&lt;p&gt;I suspected that GHC inlines lots of functions, so the idea behind the fix is to reduce inling. But how do I do this?&lt;/p&gt;

&lt;p&gt;The test suite calls multiple functions in a single &lt;code&gt;do&lt;/code&gt;-block. I can chunk this block into separate logical functions and add &lt;code&gt;{-# NOINLINE #-}&lt;/code&gt; pragmas to those functions.&lt;/p&gt;

&lt;p&gt;If needed, I can then move those functions to other modules to decrease the size of this particular module but this turned out to be not needed.&lt;/p&gt;

&lt;p&gt;Luckily, after this refactoring, I noticed lots of code duplication in the module, so I was able to remove redundant code as well.&lt;/p&gt;

&lt;p&gt;The new Haskell source code grew up to 288 LOC but the generated Core became 1900 LOC which is only 6.5x increase. Much nicer!&lt;/p&gt;

&lt;p&gt;As an additional benefit, the relevant parts of this scenario test became more visible which makes understanding, extending and fixing the test suite in the future much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, I explained how you can use GHC Core to diagnose some non-trivial problems, and described a solution for this particular problem.&lt;/p&gt;

&lt;p&gt;However, I'm still surprised to encounter such a problem in the first place. The offending module didn't look particular scary and didn't use any fancy GHC features. I was frustrated to see such a problem on a code many people would classify as "boring Haskell".&lt;/p&gt;

&lt;p&gt;Moreover, the investigation of the problem required to dive into the internals of the GHC optimiser. I personally don't think that knowledge of such details for a mundane task like a GHC upgrade is a reasonable expectation from an average Haskell developer. Stuff like this doesn't help to convince people to use Haskell 😮‍💨&lt;/p&gt;

&lt;p&gt;On the bright side, I learned something new while working on the problem. Besides, I highlighted only the problem, while I had much more enjoying moments when using Haskell!&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>functional</category>
      <category>testing</category>
      <category>programming</category>
    </item>
    <item>
      <title>New OSS rule: Do as little work as possible</title>
      <dc:creator>Dmitrii Kovanikov</dc:creator>
      <pubDate>Sun, 26 Mar 2023 13:17:17 +0000</pubDate>
      <link>https://dev.to/chshersh/new-oss-rule-do-as-little-work-as-possible-4j2k</link>
      <guid>https://dev.to/chshersh/new-oss-rule-do-as-little-work-as-possible-4j2k</guid>
      <description>&lt;p&gt;With time, my views on how to do OSS changed. &lt;/p&gt;

&lt;p&gt;Previously, my motto was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💪 Do this extra bit of work, so you won't be ashamed of the quality of your OSS projects&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, I'm more like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🦥 Choose the approach that requires you to do as less work as possible&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'll give an example of this.&lt;/p&gt;




&lt;p&gt;So, in Haskell, versions of the standard library &lt;code&gt;base&lt;/code&gt; are tightly coupled with the versions of the compiler GHC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;base-4.17.0.0&lt;/code&gt; goes with GHC 9.4&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;base-4.18.0.0&lt;/code&gt; goes with GHC 9.6&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;base-4.19.0.0&lt;/code&gt; goes with GHC 9.8&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and so on.&lt;/p&gt;

&lt;p&gt;Previously, I always specified tight lower and upper bounds on base in my project configurations, e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;depends&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;4.15&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;4.19&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it was done precisely due to this way of thinking: "do this extra work for extra quality".&lt;/p&gt;

&lt;p&gt;You see, I didn't want users of my Haskell packages to compile it with the new GHC version (and a new version of &lt;code&gt;base&lt;/code&gt;) and see errors.&lt;/p&gt;

&lt;p&gt;They would think that my project is bad, which is not! It's Haskell who's bad for breaking my stuff!! But users don't know that. So my reputation would suffer.&lt;/p&gt;

&lt;p&gt;Instead, I specified tight upper bounds on &lt;code&gt;base&lt;/code&gt;, so I could say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🙅 "You can't build my project because it doesn't explicitly support the new compiler version. I'll support it when I have time. Period."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you can imagine, this resulted in HUGE, GIGANTIC, UNACCEPTABLE amount of work, like holy f*** 🤯🗻&lt;/p&gt;

&lt;p&gt;For 40 packages and 3 releases of GHC, I had to open 120 PRs over time (sick !!!).&lt;/p&gt;

&lt;p&gt;No wonder I burnt out 😮‍💨&lt;/p&gt;




&lt;p&gt;Now, there's a solution. Versions of base in Haskell probably always will be in a form like &lt;code&gt;4.*&lt;/code&gt;. So I can specify the upper bound like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;depends&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;4.15&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And forget about ever updating upper bounds!&lt;/p&gt;

&lt;p&gt;In fact, this is rather common practice in the Haskell community, and lots of packages already do this stuff. But it took me a while to get here.&lt;/p&gt;

&lt;p&gt;This approach means that if there're no breaking changes in either &lt;code&gt;base&lt;/code&gt; or GHC, my package will continue building. And I don't have to do anything to support a new version!&lt;/p&gt;

&lt;p&gt;If something breaks, somebody will open an issue in my GitHub repository. But I still don't have to do anything, I can just ignore it for the time being! And I can support the new version when I have the time.&lt;/p&gt;

&lt;p&gt;So it's a win-win.&lt;/p&gt;




&lt;p&gt;This approach is partially inspired by my recent usage of the &lt;a href="https://hackage.haskell.org/package/pretty-terminal"&gt;pretty-terminal&lt;/a&gt; Haskell library.&lt;/p&gt;

&lt;p&gt;It's first and only version was uploaded to Hackage in 2018 (5 years ago), and it still works without requiring any changes to it!&lt;/p&gt;

&lt;p&gt;Now, that must feel good 😌 &lt;/p&gt;

</description>
      <category>opensource</category>
      <category>haskell</category>
    </item>
    <item>
      <title>SQLite type system is the wooorst</title>
      <dc:creator>Dmitrii Kovanikov</dc:creator>
      <pubDate>Sat, 11 Mar 2023 14:05:16 +0000</pubDate>
      <link>https://dev.to/chshersh/sqlite-type-system-is-the-wooorst-mi7</link>
      <guid>https://dev.to/chshersh/sqlite-type-system-is-the-wooorst-mi7</guid>
      <description>&lt;p&gt;🔮 It's not a mystery that SQLite's type system is.. not its strong side. But you probably don't realise the depth of this ocean of shit 💩🌊&lt;/p&gt;

&lt;p&gt;☕️ Grab a cup of tea and join me on this exciting journey about some "wonderful" design decisions in software engineering ⛵️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fot6rgpdvroaypfp7jmtc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fot6rgpdvroaypfp7jmtc.png" alt="SQLite types are the wooooorst" width="640" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🕗 So, you want to store timestamps in your database. &lt;/p&gt;

&lt;p&gt;Nobody has the time to read manuals. So, after browsing StackOverflow answers for about 5 minutes, you'll come up with a schema like the one below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6up2oibs631t1w8053g2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6up2oibs631t1w8053g2.png" alt="SQLite schema example" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🍶 Because software is not doomed (yet), this actually works! Good. It means we can continue with StackOverflow-Driven Development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0affv4avy834diufl46x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0affv4avy834diufl46x.png" alt="SQLite usage example" width="617" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔞You may actually find some time to read SQLite manuals in your life. You know, during one of those days while you're waiting for the code compilation to finish and all tweets are already written and sent.&lt;/p&gt;

&lt;p&gt;You'll learn that SQLite doesn't actually have the &lt;code&gt;TIMESTAMP&lt;/code&gt; type 🤯&lt;/p&gt;

&lt;p&gt;👓 You've read the previous tweet correctly. That's right. There's no &lt;code&gt;TIMESTAMP&lt;/code&gt; type. SQLite only can store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NULL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INTEGER&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REAL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TEXT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BLOB&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You see — no &lt;code&gt;TIMESTAMP&lt;/code&gt; 🙅‍♀️  Your entire life was a lie 🎂&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1xt89rdyb7be27pln7h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1xt89rdyb7be27pln7h.png" alt="SQLite types" width="800" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🧩 And yet, things somehow work. If you like puzzles, you may guess correctly that time is stored as a value of type &lt;code&gt;TEXT&lt;/code&gt;. And, again, this would be a lucky guess🍀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxivumucivi09794ya3ei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxivumucivi09794ya3ei.png" alt="Another SQLite example" width="800" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📜 A sidenote. Those strings are in the ISO8601 format. The neat thing about this format is that you can use natural text comparison aka lexicographical sorting to order your data by timestamp even though it's just text.&lt;/p&gt;

&lt;p&gt;Aren't computers cool???&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forzxpa45nrw73vcmqvz4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Forzxpa45nrw73vcmqvz4.png" alt="Don't store times in SQLite" width="500" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👷 If you haven't got burnout or depression after working in tech, it means that you will continue compiling code and having even more free time to read SQLite manuals.&lt;/p&gt;

&lt;p&gt;You'll be asking uncomfortable questions like "How does SQLite decide that &lt;code&gt;TIMESTAMP&lt;/code&gt; should be &lt;code&gt;TEXT&lt;/code&gt;?"🤔&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bnv4pqd70iwp31ply71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bnv4pqd70iwp31ply71.png" alt="Is this meme" width="556" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👴 Remember kids. Don't ask questions if you're not ready for answers. Especially in tech. 👆&lt;/p&gt;

&lt;p&gt;Turns out, SQLite contains many surprises 💩&lt;/p&gt;

&lt;p&gt;The resulting storage type (called &lt;strong&gt;affinity&lt;/strong&gt;) is defined by checking if the type name contains one of the specified strings as substrings 🤦‍♀️&lt;/p&gt;

&lt;p&gt;I have no words, only emotions (and they are not pleasant).&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1361022421021696003-316" src="https://platform.twitter.com/embed/Tweet.html?id=1361022421021696003"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1361022421021696003-316');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1361022421021696003&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;The Hindley-Milner type system? Don't laugh my socks off! 🧦🤣&lt;/p&gt;

&lt;p&gt;How about the Knuth-Morris-Pratt type system?? 😤💪&lt;/p&gt;

&lt;p&gt;🧠💠 Once you started to piece together your brain after it had been shattered, you may even try to make sense of this world.&lt;/p&gt;

&lt;p&gt;You may even think that &lt;code&gt;TIMESTAMP&lt;/code&gt; must have TEXT affinity obviously!&lt;/p&gt;

&lt;p&gt;Prepare to get f*cked so hard, you might even regret becoming a Software Engineer😨&lt;/p&gt;

&lt;p&gt;📐Turns out, if you read the affinity rules carefully, &lt;code&gt;TIMESTAMP&lt;/code&gt; is not &lt;code&gt;TEXT&lt;/code&gt;. It has the &lt;code&gt;NUMERIC&lt;/code&gt; affinity in fact!&lt;/p&gt;

&lt;p&gt;If you're still alive after learning that a &lt;code&gt;TIMESTAMP&lt;/code&gt; is actually &lt;code&gt;NUMERIC&lt;/code&gt; and stores &lt;code&gt;TEXT&lt;/code&gt; inside, congratulations 👏&lt;/p&gt;

&lt;p&gt;But that's not the end of the story.&lt;/p&gt;

&lt;p&gt;Listen, the thing is... A while ago, some genius decided that a column of the &lt;code&gt;NUMERIC&lt;/code&gt; affinity can store values of any type inside.&lt;/p&gt;

&lt;p&gt;What a piece of joke 🤡&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F920fvcqzu69nmr9624rq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F920fvcqzu69nmr9624rq.png" alt="Another SQLite example" width="800" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SQLite has 5 different affinities:&lt;/p&gt;

&lt;p&gt;🟡TEXT&lt;br&gt;
🟣NUMERIC&lt;br&gt;
🔴INTEGER&lt;br&gt;
🟠REAL&lt;br&gt;
🔵BLOB&lt;/p&gt;

&lt;p&gt;And no, &lt;code&gt;NUMERIC&lt;/code&gt; and &lt;code&gt;INTEGER&lt;/code&gt; are not the same. &lt;code&gt;NUMERIC&lt;/code&gt; is a synonym for ANY (because why not?).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftejlwt0y7pppcgqbb1kj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftejlwt0y7pppcgqbb1kj.png" alt="Stick meme" width="500" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Legend says, if you collect all 5 storage classes under the &lt;code&gt;NUMERIC&lt;/code&gt; affinity in the same column, you'll be able to erase half of your data in an SQLite database with a snap of a finger 🟡🟣🔴🟠🔵🟢&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fle9ods6fcygd7bcovcmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fle9ods6fcygd7bcovcmp.png" alt="Stones of eternity" width="500" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🚲🏏 If you're missing good old &lt;code&gt;CastFailedException&lt;/code&gt;, SQLite is your database of choice 👍&lt;/p&gt;

&lt;p&gt;To summarise, any column of type &lt;code&gt;TIMESTAMP&lt;/code&gt;, &lt;code&gt;TIME&lt;/code&gt;, &lt;code&gt;DATE&lt;/code&gt;, &lt;code&gt;DATETIME&lt;/code&gt;, etc. actually has the NUMERIC affinity and can store &lt;strong&gt;anything&lt;/strong&gt; inside. So you better be careful!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkaokvmx0auhtrm15p7q1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkaokvmx0auhtrm15p7q1.png" alt="Let's see who this really is" width="500" height="666"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;But let's end on a positive note🎶&lt;br&gt;
People slowly but surely see the value of strong static typing even if some are still trying to slow down the progress.&lt;/p&gt;

&lt;p&gt;Since SQLite version 3.37 or higher, you can define tables in the STRICT mode to live happily!🥳&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.sqlite.org/stricttables.html"&gt;SQLite: Strict Tables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That's all for me! Hope you enjoyed this post and had a good laugh 🤗 SQLite is actually amazing despite some design decisions!&lt;/p&gt;

&lt;p&gt;Till the next time when I share a story with you about how I learned that it's &lt;strong&gt;impossible&lt;/strong&gt; to have an in-memory DB in SQLite with multiple concurrent read-only queries 👋&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqywtl3td0sbwpevzbiqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqywtl3td0sbwpevzbiqj.png" alt="Skeletor disturbing facts" width="500" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post is a copy of my Twitter thread from May 24, 2022 with a few changes&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1529010669642727424-274" src="https://platform.twitter.com/embed/Tweet.html?id=1529010669642727424"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1529010669642727424-274');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1529010669642727424&amp;amp;theme=dark"
  }



 &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>sqlite</category>
      <category>rant</category>
      <category>funny</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
