<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Chris Wendt</title>
    <description>The latest articles on Forem by Chris Wendt (@chrismwendt).</description>
    <link>https://forem.com/chrismwendt</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%2F355582%2F1554888c-8b13-4b9c-8f27-a80007e20a81.jpeg</url>
      <title>Forem: Chris Wendt</title>
      <link>https://forem.com/chrismwendt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/chrismwendt"/>
    <language>en</language>
    <item>
      <title>How to auto-shutdown GCP VMs and save $</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Fri, 02 Dec 2022 03:41:32 +0000</pubDate>
      <link>https://forem.com/chrismwendt/how-to-auto-shutdown-gcp-vms-and-save--pe1</link>
      <guid>https://forem.com/chrismwendt/how-to-auto-shutdown-gcp-vms-and-save--pe1</guid>
      <description>&lt;p&gt;The following script will shut down an instance that hasn't had any SSH connections in a while. The debounce defaults to 300 seconds.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy these files into your VM:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;/usr/bin/auto-shutdown.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IDLE_TIMEOUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;600&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;

    &lt;span class="n"&gt;idle_since&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getBootTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;last -F | jc --last&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;still logged in&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;idle_since&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idle_since&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logout_epoch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;idle_since&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idle_since&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logout_epoch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="n"&gt;idle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;idle_since&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;idle&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Idle for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;idle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s, shutting down...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shutdown now&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Idle for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;idle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s, checking again in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getBootTime&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;uptime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/proc/uptime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uptime&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/etc/systemd/system/auto-shutdown.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Auto-shutdown VM

[Service]
ExecStart=/usr/bin/auto-shutdown.py
Restart=always
Environment=PYTHONUNBUFFERED=true

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;pip3 install jc&lt;/code&gt; and if &lt;code&gt;which jc&lt;/code&gt; shows conda, then &lt;code&gt;sudo ln -sf /opt/conda/bin/jc /usr/bin/jc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo chmod u+x /usr/bin/auto-shutdown.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo systemctl enable auto-shutdown.service&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sudo systemctl restart auto-shutdown&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;systemctl status auto-shutdown.service&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/ravwojdyla/05249805680f5cd8b54b6f01cd0c383b" rel="noopener noreferrer"&gt;Inspiration&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Faster builds with VMs than with Google Cloud Build</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Fri, 02 Dec 2022 03:36:26 +0000</pubDate>
      <link>https://forem.com/chrismwendt/faster-builds-with-vms-than-with-google-cloud-build-4m1p</link>
      <guid>https://forem.com/chrismwendt/faster-builds-with-vms-than-with-google-cloud-build-4m1p</guid>
      <description>&lt;p&gt;Google Cloud Build has to pull your Docker image on every build, which can be slow if the image is 1GB+ (e.g. when using &lt;a href="https://dev.to/chrismwendt/wasm-difficulties-in-rust-haskell-and-go-5f8m"&gt;cutting edge Wasm tooling&lt;/a&gt; that doesn't work on all OSes/architectures yet).&lt;/p&gt;

&lt;p&gt;Here's how to speed up your builds using plain VMs instead:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;initiate-build.sh&lt;/code&gt; creates/starts/resumes the build VM if it's nonexistent/stopped/suspended, copies a &lt;code&gt;Dockerfile&lt;/code&gt; and context, then runs &lt;code&gt;build.py&lt;/code&gt; on the VM and copies the artifact back to your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-xeuo&lt;/span&gt; pipefail

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INSTANCE&lt;/span&gt;:&lt;span class="p"&gt;=some-build-vm&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

status&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  gcloud compute instances list &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"value(status)"&lt;/span&gt; &lt;span class="nt"&gt;--filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"name=&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;status&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  gcloud compute instances create &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--machine-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;n2-standard-2 &lt;span class="nt"&gt;--create-disk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;boot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;,image&lt;span class="o"&gt;=&lt;/span&gt;projects/ubuntu-os-cloud/global/images/ubuntu-2210-kinetic-amd64-v20221101,size&lt;span class="o"&gt;=&lt;/span&gt;100
  &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="s2"&gt;"SUSPENDED"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  gcloud compute instances resume &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="s2"&gt;"STOPPED"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  gcloud compute instances start &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="s2"&gt;"RUNNING"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unrecognized instance state &lt;/span&gt;&lt;span class="nv"&gt;$state&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;

&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; scratch
&lt;span class="nb"&gt;mkdir &lt;/span&gt;scratch
&lt;span class="nb"&gt;cp &lt;/span&gt;setup.py auto-shutdown.py auto-shutdown.service Dockerfile otherfiles scratch
gcloud compute scp &lt;span class="nt"&gt;--recurse&lt;/span&gt; scratch &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"~"&lt;/span&gt;
gcloud compute ssh &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;'bash -c "( cd scratch &amp;amp;&amp;amp; python3 setup.py ); rm -rf scratch"'&lt;/span&gt;
gcloud compute scp &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INSTANCE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"~/artifact"&lt;/span&gt; artifact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;build.py&lt;/code&gt; installs Docker if needed, sets up an auto-shutdown after 5mins of inactivity (to mimic the serverless nature of Google Cloud Build), builds the Docker image, then copies the artifact out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;check_call&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker.io&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo chmod 755 auto-shutdown.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo mv auto-shutdown.py /usr/bin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo mv auto-shutdown.service /etc/systemd/system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo systemctl daemon-reload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo systemctl restart auto-shutdown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo docker build -t img .&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo docker run --rm --entrypoint cat img /artifact &amp;gt; ~/artifact&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;which&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CalledProcessError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo apt-get update&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;check_call&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sudo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apt-get&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;install&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://dev.to/chrismwendt/how-to-auto-shutdown-gcp-vms-and-save--pe1"&gt;auto-shutdown&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>The state of Wasm in Rust, Haskell, and Go</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Thu, 01 Dec 2022 05:18:57 +0000</pubDate>
      <link>https://forem.com/chrismwendt/wasm-difficulties-in-rust-haskell-and-go-5f8m</link>
      <guid>https://forem.com/chrismwendt/wasm-difficulties-in-rust-haskell-and-go-5f8m</guid>
      <description>&lt;h1&gt;
  
  
  2023
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Haskell
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;a href="https://github.com/alpmestan/ghc.nix" rel="noopener noreferrer"&gt;ghc.nix&lt;/a&gt; to build a GHC version that targets Wasm&lt;/li&gt;
&lt;li&gt;It supports WASI&lt;/li&gt;
&lt;li&gt;It works on Mac M1&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Go
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Go 1.21 added experimental support for WASI (&lt;code&gt;GOOS=wasip1&lt;/code&gt;, &lt;code&gt;GOARCH=wasm&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;tinygo: JSON packages now work (tested on 0.29.0)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  2022
&lt;/h1&gt;

&lt;p&gt;Compiling to Webassembly (Wasm) is still rather difficult IMO in 2022. Here I document some of my findings:&lt;/p&gt;

&lt;h2&gt;
  
  
  Haskell
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Compiling to Wasm requires an early release of GHC, &lt;a href="https://www.tweag.io/blog/2022-11-22-wasm-backend-merged-in-ghc/" rel="noopener noreferrer"&gt;slated for GHC 9.6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It supports WASI&lt;/li&gt;
&lt;li&gt;It does not work on ARM, such as Mac M1&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rust
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;wasm32-unknown-unknown&lt;/code&gt; target works, but requires low-level memory management and &lt;code&gt;unsafe&lt;/code&gt; blocks&lt;/li&gt;
&lt;li&gt;A bunch of packages like tokio don't work because they transitively depend on &lt;code&gt;net&lt;/code&gt;, and WASI doesn't have networking yet (&lt;a href="https://github.com/WebAssembly/wasi-sockets/tree/cc80e7ad6190ab872b64a3be11b00ed5276df5f1" rel="noopener noreferrer"&gt;networking&lt;/a&gt; is in &lt;a href="https://github.com/WebAssembly/meetings/blob/452e4e36a7c7b539bb6a3e9bca7f6ac901b5f35b/process/phases.md" rel="noopener noreferrer"&gt;phase 1&lt;/a&gt; of 5), and it doesn't seem possible to turn off the &lt;code&gt;net&lt;/code&gt; feature of transitive dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Go
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The main Go compiler &lt;a href="https://github.com/golang/go/issues/31105" rel="noopener noreferrer"&gt;doesn't yet support WASI&lt;/a&gt;, it only supports targeting JavaScript environments (see how &lt;a href="https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63#support-grid-1" rel="noopener noreferrer"&gt;the only GOOS/GOARCH pair is js/wasm&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;TinyGo supports WASI &lt;code&gt;tinygo build -target wasi .&lt;/code&gt;, but a ton of packages like &lt;code&gt;encoding/json&lt;/code&gt; don't work because not all of the &lt;code&gt;reflect&lt;/code&gt; stdlib package has been implemented in the TinyGo runtime. I tried these alternative JSON packages:

&lt;ul&gt;
&lt;li&gt;✅ &lt;a href="https://github.com/mailru/easyjson" rel="noopener noreferrer"&gt;easyjson&lt;/a&gt; worked&lt;/li&gt;
&lt;li&gt;❌ &lt;a href="https://github.com/pquerna/ffjson" rel="noopener noreferrer"&gt;ffjson&lt;/a&gt; failed to compile, last commit 2019&lt;/li&gt;
&lt;li&gt;❌ &lt;a href="https://github.com/ugorji/go" rel="noopener noreferrer"&gt;go-codec&lt;/a&gt; depends on &lt;code&gt;reflect&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;❌ &lt;a href="https://github.com/valyala/fastjson" rel="noopener noreferrer"&gt;fastjson&lt;/a&gt; can decode, but can't encode&lt;/li&gt;
&lt;li&gt;❌ &lt;a href="https://github.com/buger/jsonparser" rel="noopener noreferrer"&gt;jsonparser&lt;/a&gt; can decode, but can't encode&lt;/li&gt;
&lt;li&gt;❌ &lt;a href="https://github.com/Jeffail/gabs" rel="noopener noreferrer"&gt;gabs&lt;/a&gt; depends on &lt;code&gt;reflect&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>How to give Docker Desktop more RAM/swap than the limit</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Sat, 26 Nov 2022 21:28:41 +0000</pubDate>
      <link>https://forem.com/chrismwendt/how-to-give-docker-desktop-more-ramswap-than-the-limit-2cm4</link>
      <guid>https://forem.com/chrismwendt/how-to-give-docker-desktop-more-ramswap-than-the-limit-2cm4</guid>
      <description>&lt;p&gt;Docker Desktop on Mac has a limit of 8GB RAM and 4GB of swap:&lt;/p&gt;

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

&lt;p&gt;You can allocate even more swap space by running these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host$ docker run --rm -ti -v swap:/swap --privileged
root@91dd1647484a:/# fallocate -l 4G /swap/file
root@91dd1647484a:/# chmod 600 /swap/file
root@91dd1647484a:/# mkswap /swap/file
Setting up swapspace version 1, size = 4 GiB (4294963200 bytes)
no label, UUID=2e028e57-9572-4bed-94c8-093ae1af1211
root@91dd1647484a:/# swapon /swap/file
root@91dd1647484a:/# free -m
               total        used        free      shared  buff/cache   available
Mem:            7951         280        7083         321         587        7191
Swap:           8191           0        8191
root@91dd1647484a:/#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The swap space will be available to all containers. It also persists across restarts.&lt;/p&gt;

&lt;p&gt;To delete the swap space, run &lt;code&gt;docker volume rm swap&lt;/code&gt; then restart Docker Desktop. You will get an error saying it couldn't be deleted, but after the restart it will be gone.&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
    <item>
      <title>How to consolidate cleanup logic using the "with" function pattern</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Sat, 16 Jul 2022 04:19:00 +0000</pubDate>
      <link>https://forem.com/chrismwendt/how-to-consolidate-cleanup-logic-using-the-with-function-pattern-45nm</link>
      <guid>https://forem.com/chrismwendt/how-to-consolidate-cleanup-logic-using-the-with-function-pattern-45nm</guid>
      <description>&lt;p&gt;A few of my teammates have liked the "with" function pattern and how convenient it is for ensuring proper cleanup of resources.&lt;/p&gt;

&lt;p&gt;A "with" function takes a callback and performs some initialization, calls the callback, then performs some cleanup.&lt;/p&gt;

&lt;p&gt;For example, instead of manually doing a &lt;code&gt;COMMIT&lt;/code&gt; or &lt;code&gt;ROLLBACK&lt;/code&gt; for every SQL transaction like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"INSERT ..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// 💥 Oops, forgot tx.Rollback(), tx remains open!&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// execute more statements...&lt;/span&gt;

&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can create a &lt;code&gt;withTx()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;withTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rollback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Commit&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;And use it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;withTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="n"&gt;Tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"INSERT ..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// 🙂 No need to remember to call tx.Rollback()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// execute more statements...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At a glance, ~5% of &lt;a href="https://sourcegraph.com/search?q=context:global+mattn/go-sqlite3+AND+Begin+NOT+Rollback+lang:go&amp;amp;patternType=literal" rel="noopener noreferrer"&gt;calls&lt;/a&gt; to &lt;code&gt;Begin()&lt;/code&gt; appear to be missing a corresponding &lt;code&gt;Rollback()&lt;/code&gt; on error (&lt;a href="https://github.com/owncast/owncast/blob/fb3973b264d82aad7e0ae3086255d72506a38c41/core/data/persistence.go#L86" rel="noopener noreferrer"&gt;likely example&lt;/a&gt;). Using a &lt;code&gt;withTx()&lt;/code&gt; function would help eliminate those bugs.&lt;/p&gt;

&lt;p&gt;And with Go generics we can return a value from &lt;code&gt;withTx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-func withTx(db DB, callback func(Tx) error) error {
&lt;/span&gt;&lt;span class="gi"&gt;+func withTx[T any](db DB, callback func(Tx) (*T, error)) (*T, error) {
&lt;/span&gt;    tx, err := db.Begin()
    if err != nil {
&lt;span class="gd"&gt;-       return err
&lt;/span&gt;&lt;span class="gi"&gt;+       return nil, err
&lt;/span&gt;    }
    defer tx.Rollback()
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-   err = callback(tx)
&lt;/span&gt;&lt;span class="gi"&gt;+   t, err := callback(tx)
&lt;/span&gt;    if err != nil {
        return err
    }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-   return tx.Commit()
&lt;/span&gt;&lt;span class="gi"&gt;+   return t, tx.Commit()
&lt;/span&gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits of the "with" function pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic initialization and cleanup&lt;/li&gt;
&lt;li&gt;Less code&lt;/li&gt;
&lt;li&gt;In some cases, there's no need to check if the operation succeeded and do something different if it didn't&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One caveat to bear in mind: you have to make sure the callback does not return the resource, otherwise that resource could be used after the clean up runs and cause an error! I don't think it's possible to enforce correct use at compile time in Go. It is possible (but not ergonomic) in &lt;a href="https://www.snoyman.com/blog/2018/10/raii-better-than-bracket-pattern/" rel="noopener noreferrer"&gt;Haskell&lt;/a&gt; and it's convenient in &lt;a href="https://www.snoyman.com/blog/2018/10/raii-better-than-bracket-pattern/#rust" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; by piggy-backing on the built-in value lifetime machinery.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Ergonomic error handling in Go using generics</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Thu, 07 Jul 2022 01:58:35 +0000</pubDate>
      <link>https://forem.com/chrismwendt/ergonomic-error-handling-in-go-using-generics-17b9</link>
      <guid>https://forem.com/chrismwendt/ergonomic-error-handling-in-go-using-generics-17b9</guid>
      <description>&lt;p&gt;Now that Go has generics, I discovered that you can implement ergonomic exception-like error handling as a library. Here's my take on the idea:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/chrismwendt/go-exceptions" rel="noopener noreferrer"&gt;https://github.com/chrismwendt/go-exceptions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typical error-handling in Go looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;g1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;g2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a few downsides to handling errors like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;if err != nil { return err }&lt;/code&gt; takes 3 extra lines&lt;/li&gt;
&lt;li&gt;You need to explicitly store the errors in variables and conditionally return them every time you call a function that returns an error&lt;/li&gt;
&lt;li&gt;Error handling ends up cluttering the code and making it harder to focus on the domain logic&lt;/li&gt;
&lt;li&gt;You can't do &lt;code&gt;f(g())&lt;/code&gt; if &lt;code&gt;g()&lt;/code&gt; returns a value and an error and &lt;code&gt;f()&lt;/code&gt; expects only a value, which forces you to split up the code, add temporary variables, add an &lt;code&gt;if&lt;/code&gt;, and manually handle the error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How can we make this more ergonomic? Perhaps ideally, Go would add support for &lt;a href="https://stackoverflow.com/a/42921174/2061958" rel="noopener noreferrer"&gt;Rust's &lt;code&gt;?&lt;/code&gt; operator&lt;/a&gt;, but we can get pretty close using &lt;a href="https://gobyexample.com/panic" rel="noopener noreferrer"&gt;&lt;code&gt;panic()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://gobyexample.com/recover" rel="noopener noreferrer"&gt;&lt;code&gt;recover()&lt;/code&gt;&lt;/a&gt; to have errors bubble up without needing to explicitly store them in variables.&lt;/p&gt;

&lt;p&gt;Here's what usage of go-exceptions looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="s"&gt;"github.com/chrismwendt/go-exceptions"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// Catch() calls recover() and assigns the error to &amp;amp;err&lt;/span&gt;
  &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;// Throw() calls panic() if the error argument is not nil&lt;/span&gt;
  &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g1&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

  &lt;span class="c"&gt;// Throw() also accepts a label&lt;/span&gt;
  &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g2&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"g2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;// Throw1() returns 1 value, Throw2() returns 2 values, etc.&lt;/span&gt;
  &lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throw1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g3&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

  &lt;span class="c"&gt;// Passing arguments is easier&lt;/span&gt;
  &lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throw1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g5&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;

  &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is more ergonomic in a few ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It eliminates a bunch of temporary &lt;code&gt;err&lt;/code&gt; variables&lt;/li&gt;
&lt;li&gt;You can more easily pass the result of one function into another&lt;/li&gt;
&lt;li&gt;The code is more compact&lt;/li&gt;
&lt;li&gt;Once you learn about the library, IMO the code is more readable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Internally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Throw(err)&lt;/code&gt; is basically: &lt;code&gt;if err != nil { panic(err) }&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Catch(*error)&lt;/code&gt; recovers from any panic and stores the recovered value in the given error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You could go a bit further and adopt a convention of panicking rather than returning errors, but that begins to depart from typical Go calling conventions and could cause friction when calling or being called by other code.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Limitations of Go generics</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Mon, 13 Jun 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/chrismwendt/limitations-of-go-generics-5834</link>
      <guid>https://forem.com/chrismwendt/limitations-of-go-generics-5834</guid>
      <description>&lt;p&gt;Go does not allow type parameters (generics) in method declarations, and that limits the APIs you can make.&lt;/p&gt;

&lt;p&gt;Here's an example when you might encounter that problem and how to work around it:&lt;/p&gt;

&lt;p&gt;Say you're building a library to process streams of values, and you start with this type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Stream receives In values from upstream and&lt;/span&gt;
&lt;span class="c"&gt;// sends Out values downstream.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;In&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally, we could write the following generic &lt;code&gt;.connect()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//                           vvvvvvv ❌ not allowed by Go!&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But &lt;a href="https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#no-parameterized-methods" rel="noopener noreferrer"&gt;Go does not allow type parameters in method declarations&lt;/a&gt;. That means the &lt;code&gt;.connect()&lt;/code&gt; method can't introduce any new types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//                                                  vvvvvv ✅ OK&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That restricts the kinds of processing we can do by chaining &lt;code&gt;.connect()&lt;/code&gt; methods together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;lines&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;omitEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// ✅ (string -&amp;gt; string) same type&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// ❌ (string -&amp;gt; int   ) new type&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can get around this limitation by converting &lt;code&gt;.connect()&lt;/code&gt; into a top-level &lt;code&gt;connect()&lt;/code&gt; function that can change the stream type 🎉:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;
    &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, readability suffers a little:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omitEmpty&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;getLength&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or written with right-associativity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;omitEmpty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or on separate lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;
&lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omitEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s4&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need separate variables for each step in the stream, but other than that it's not much different from method chaining above!&lt;/p&gt;

</description>
      <category>go</category>
      <category>generics</category>
    </item>
    <item>
      <title>Cleaning up threads in Haskell</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Sun, 31 May 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/chrismwendt/cleaning-up-threads-in-haskell-1ng6</link>
      <guid>https://forem.com/chrismwendt/cleaning-up-threads-in-haskell-1ng6</guid>
      <description>&lt;p&gt;When you press Ctrl+C in ghci or when your code gets reloaded in ghcid, only the main thread is killed (via a &lt;a href="https://hackage.haskell.org/package/base-4.14.0.0/docs/Control-Exception.html" rel="noopener noreferrer"&gt;&lt;code&gt;UserInterrupt&lt;/code&gt;&lt;/a&gt; exception). All other spawned threads continue to run. Here's how to clean them up:&lt;/p&gt;

&lt;p&gt;Since the addition of &lt;a href="https://downloads.haskell.org/ghc/9.6.1-rc1/docs/libraries/base-4.18.0.0/GHC-Conc.html#v:listThreads" rel="noopener noreferrer"&gt;&lt;code&gt;listThreads&lt;/code&gt;&lt;/a&gt; in GHC 9.6.1, you can label your threads with &lt;a href="https://downloads.haskell.org/ghc/9.6.1-rc1/docs/libraries/base-4.18.0.0/GHC-Conc.html#v:labelThread" rel="noopener noreferrer"&gt;&lt;code&gt;labelThread&lt;/code&gt;&lt;/a&gt; then list all, filter, and kill them later. This won't work for threads spawned by dependencies since they're probably not labeled. Perhaps a labeling convention could emerge to support this use case, or GHC could apply descriptive labels to all threads to enable user code to disambiguate between user threads and runtime threads (e.g. the IO manager). One hacky idea that doesn't need labels is to sort the list of threads, drop the first 3 (which probably correspond to runtime threads, dangerous assumption though), and kill the rest.&lt;/p&gt;

&lt;p&gt;Prior to GHC 9.6.1, it &lt;a href="https://stackoverflow.com/questions/24999636/is-there-a-way-to-kill-all-forked-threads-in-a-ghci-session-without-restarting-i" rel="noopener noreferrer"&gt;was not possible to list threads&lt;/a&gt;, so it was up to you the programmer to clean up. Here's an idea for cleaning up at least some threads (although it suffers from the same shortcoming as above in that it can't catch threads spawned by dependencies):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep track of all currently-running threads in an &lt;code&gt;IORef (Set (Async a))&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When an exception occurs in any thread, hand it to the main thread with &lt;code&gt;putMVar exceptionVar exception&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Have the main thread wait for an exception then kill all currently-running threads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementation:&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;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;safeAsync&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;runApp&lt;/span&gt;

  &lt;span class="n"&gt;dieOnException&lt;/span&gt;

&lt;span class="c1"&gt;-- NOINLINE ensures that there is only one MVar&lt;/span&gt;
&lt;span class="cp"&gt;{-# NOINLINE errorVar #-}&lt;/span&gt;
&lt;span class="n"&gt;errorVar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafePerformIO&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;newEmptyMVar&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;MVar&lt;/span&gt; &lt;span class="kt"&gt;SomeException&lt;/span&gt;

&lt;span class="c1"&gt;-- Set of currently-running threads&lt;/span&gt;
&lt;span class="cp"&gt;{-# NOINLINE asyncsRef #-}&lt;/span&gt;
&lt;span class="n"&gt;asyncsRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unsafePerformIO&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;newIORef&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IORef&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Async&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;-- Waits for an exception the kills all threads&lt;/span&gt;
&lt;span class="n"&gt;dieOnException&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readMVar&lt;/span&gt; &lt;span class="n"&gt;errorVar&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;mapM_&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;=&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;readIORef&lt;/span&gt; &lt;span class="n"&gt;asyncsRef&lt;/span&gt;
  &lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="s"&gt;"exiting entire Haskell process"&lt;/span&gt;
  &lt;span class="n"&gt;exitFailure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- A version of `async` that additionally stores itself in the currently-running threads.&lt;/span&gt;
&lt;span class="c1"&gt;-- It also notifies the main thread when there's an exception.&lt;/span&gt;
&lt;span class="n"&gt;safeAsync&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;asyncValue&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
  &lt;span class="n"&gt;forkIO&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt; &lt;span class="n"&gt;asyncValue&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atomicModifyIORef'&lt;/span&gt; &lt;span class="n"&gt;asyncsRef&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;asyncValue&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;catches&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Handler&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;e&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;AsyncCancelled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Handler&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;e&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;SomeException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;putMVar&lt;/span&gt; &lt;span class="n"&gt;errorVar&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;atomicModifyIORef'&lt;/span&gt; &lt;span class="n"&gt;asyncsRef&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt; &lt;span class="n"&gt;asyncValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;return&lt;/span&gt; &lt;span class="n"&gt;asyncValue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>haskell</category>
      <category>concurrency</category>
      <category>threads</category>
    </item>
    <item>
      <title>The cost of enforcing purity in Haskell</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Wed, 27 May 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/chrismwendt/the-cost-of-enforcing-purity-in-haskell-4idd</link>
      <guid>https://forem.com/chrismwendt/the-cost-of-enforcing-purity-in-haskell-4idd</guid>
      <description>&lt;p&gt;&lt;a href="https://degoes.net/articles/no-effect-tracking" rel="noopener noreferrer"&gt;"Effect Tracking Is Commercially Worthless"&lt;/a&gt; argues that enforcing purity is not commercially valuable, and this struck a chord with with me because I had already recently started to question the usefulness of enforcing purity in the context of a commercial project &lt;a href="http://codewyng.io/" rel="noopener noreferrer"&gt;CodeWyng&lt;/a&gt; that I'm working on.&lt;/p&gt;

&lt;p&gt;Looking past the distractions (the tongue-in-cheek sales rhetoric, the conspicuous advertisement for the ZIO project, and the vacuous is-Haskell-really-pure debate), there are some compelling arguments against enforcing purity that I've never heard before. The novelty of the counterarguments has drawn my attention to studying this subject further.&lt;/p&gt;

&lt;p&gt;Because "effect tracking" is ambiguous and causes confusion, these terms might be useful in discussion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enforcing purity&lt;/strong&gt; prevents the use of functions that interact with the world to be used in any context that doesn't allow it. The mechanism by which purity is enforced in Haskell is a combination of the type system (e.g. you can't use &lt;code&gt;IO Int&lt;/code&gt; in a function like &lt;code&gt;sum :: [Int] -&amp;gt; Int&lt;/code&gt;) and the specific interface to &lt;code&gt;IO&lt;/code&gt; exposed to the programmer (i.e. all functions that interact with the world return something wrapped in &lt;code&gt;IO&lt;/code&gt; and there is no &lt;code&gt;IO a -&amp;gt; a&lt;/code&gt; to circumvent the intended interface, except for the backdoor &lt;code&gt;unsafePerformIO&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency injection&lt;/strong&gt; is a technique where the implementation of dependencies of a function can be changed at the call site. For example, the dependency on the database can be swapped out for a mock implementation in tests. Some mechanisms by which dependency injection can be performed include: passing an explicit argument &lt;code&gt;runApp :: DB -&amp;gt; IO ()&lt;/code&gt;, adding a type class constraint &lt;code&gt;runApp :: (HasDB m, MonadIO m) =&amp;gt; m ()&lt;/code&gt;, or mutating the global DB variable to set it to a mock implementation prior to running tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an aside, I don't think John is saying that the dependency-injection-like property of effects systems is worthless, but rather that any restriction-of-side-effects property (just like &lt;code&gt;IO&lt;/code&gt; has) is worthless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Original motivation for &lt;code&gt;IO&lt;/code&gt; in Haskell:&lt;/strong&gt; ensure that expressions that describe interactions with the world are evaluated in a deterministic order (specified by the programmer) even under lazy evaluation and in the presence of an optimizing compiler, while maintaining purity. Paper for reference: &lt;a href="https://www.microsoft.com/en-us/research/wp-content/uploads/1993/01/imperative.pdf" rel="noopener noreferrer"&gt;Imperative functional programming&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mechanism by which &lt;code&gt;IO&lt;/code&gt; enforces purity:&lt;/strong&gt; the &lt;code&gt;IO&lt;/code&gt; data type is essentially the state monad where the world is the state &lt;code&gt;data IO a = IO (World -&amp;gt; (a, World))&lt;/code&gt; and the &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;+&lt;code&gt;return&lt;/code&gt; interface exposed to the programmer only enables chaining &lt;code&gt;IO&lt;/code&gt; values together such that each value of type &lt;code&gt;World&lt;/code&gt; is used exactly once, passing it from one &lt;code&gt;IO&lt;/code&gt; action to the next in sequence. In other words, each function that takes a &lt;code&gt;World&lt;/code&gt; (e.g. &lt;code&gt;getLine :: IO String&lt;/code&gt;, &lt;code&gt;getLine :: World -&amp;gt; (String, World)&lt;/code&gt;) is never called with the same &lt;code&gt;World&lt;/code&gt; value twice. All function calls that interact with the world take a different &lt;code&gt;World&lt;/code&gt; value, and therefore it's impossible to observe the same function applied to the same arguments returning a different value. That's precisely the definition of referential transparency, A.K.A. purity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Motivation for enforcing purity:&lt;/strong&gt; I found less information on this topic, so I'm mostly going off of intuition here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;STM is guaranteed to be correct&lt;/li&gt;
&lt;li&gt;Equational reasoning always holds (i.e. factoring out common code into a binding never changes program behavior)&lt;/li&gt;
&lt;li&gt;The type of a function documents whether or not it interacts with the world&lt;/li&gt;
&lt;li&gt;Programs tend to be written in such a way that interactions with the world are more separated from logic than they would be without enforced purity&lt;/li&gt;
&lt;li&gt;It's the only lazy+pure language and a lot of learning has come out of programming with that mind-bending combination of properties (myself included)&lt;/li&gt;
&lt;li&gt;There's a pervasive undercurrent of "it's good for you" in a "slavery is freedom" sort of way, and that programmers lack the discipline required to architect large programs without the enforcement of purity&lt;/li&gt;
&lt;li&gt;Restrictions like purity or linear types enable mathematical reasoning by way of connection to logic, category theory, and algebra. &lt;a href="https://www.youtube.com/watch?v=GqmsQeSzMdw" rel="noopener noreferrer"&gt;Constraints Liberate, Liberties Constrain&lt;/a&gt; explores this tradeoff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Motivation for not enforcing purity:&lt;/strong&gt; Starting with John's reasons, with a few I thought of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If it were worthwhile, annotation processors would be in widespread use for mainstream languages and have IDE support (proof by contradiction)&lt;/li&gt;
&lt;li&gt;Tooling alone using static call tree analysis would achieve the same goal without placing a burden on the programmer&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://checkerframework.org/manual/#purity-checker" rel="noopener noreferrer"&gt;Checker framework&lt;/a&gt; has no commercial traction&lt;/li&gt;
&lt;li&gt;PureScript went from pure+tags to pure (still enforces purity, but with less granularity)&lt;/li&gt;
&lt;li&gt;Almost every other aspect of FP has been widely adopted while enforced purity has not: first-class and anonymous functions (e.g. in Java 8), lexical scoping (e.g. arrow functions for &lt;code&gt;this&lt;/code&gt; and &lt;code&gt;let&lt;/code&gt; vs &lt;code&gt;var&lt;/code&gt; in JavaScript), immutable annotations (e.g. &lt;code&gt;const&lt;/code&gt; vs &lt;code&gt;let&lt;/code&gt; in JavaScript), option chaining (e.g. C#'s and TypeScript's &lt;code&gt;?.&lt;/code&gt; operator), the &lt;code&gt;Maybe&lt;/code&gt; data type (e.g. &lt;code&gt;Optional&lt;/code&gt; in Java), parser combinators, QuickCheck, STM, list comprehensions, pattern matching, parametric polymorphism (A.K.A. generics), type inference, the list goes on...&lt;/li&gt;
&lt;li&gt;Programmers already know using intuition with a high degree of certainty whether or not a function interacts with the world. Functions that violate that intuition are avoided and are quickly rectified or worked around (e.g. &lt;a href="https://news.ycombinator.com/item?id=21765788" rel="noopener noreferrer"&gt;&lt;code&gt;java.net.URL#hashCode/equals&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Enforcing purity places burdens of boilerplate and ceremony on programmers (e.g. when to use &lt;code&gt;let&lt;/code&gt; vs &lt;code&gt;&amp;lt;-&lt;/code&gt;, doing acrobatics with &lt;code&gt;&amp;lt;$&amp;gt;&lt;/code&gt;/&lt;code&gt;fmap&lt;/code&gt;/&lt;code&gt;=&amp;lt;&amp;lt;&lt;/code&gt;/&lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;/&lt;code&gt;&amp;lt;*&amp;gt;&lt;/code&gt;/etc. to satisfy the type checker, the need to add an import of &lt;code&gt;Debug.Trace&lt;/code&gt; and use special functions to be able to inspect the execution of functions that definitely don't interact with the world, and the need to create a new variable binding for many trivial values such as &lt;code&gt;count &amp;lt;- readIORef countRef&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Telling beginners "don't worry, you don't need to understand what &lt;code&gt;IO&lt;/code&gt; is or how it works, just use these combinators you won't understand for the first 20 hours of learning Haskell" is Haskell's equivalent of "oh just ignore all that &lt;code&gt;public static void main String[] args&lt;/code&gt; stuff for now and don't forget to wrap your hello world in a class"&lt;/li&gt;
&lt;li&gt;I can't remember ever accidentally running IO at all or in the wrong order in any language (not just Haskell). It just doesn't seem to happen. I've been bitten by mutation quite a few times, but they were always a conscious choice of tradeoffs or something silly like JavaScript's &lt;code&gt;array.reverse()&lt;/code&gt; which both mutates the array and returns a reversed shallow copy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Experiment with strict impure Haskell&lt;/strong&gt; To get another data point, I'm considering modifying the Haskell backend of CodeWyng to use &lt;code&gt;LANGUAGE Strict&lt;/code&gt; and insert &lt;code&gt;unsafePerformIO&lt;/code&gt; in a bunch of places, especially at usages of &lt;code&gt;IORef&lt;/code&gt;s and &lt;code&gt;MVar&lt;/code&gt;s to see 1) if it works 2) if there are any bugs 3) if it's easier or more intuitive to program that way. Will post back here if/when I do. &lt;strong&gt;Update:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding &lt;code&gt;{-# LANGUAGE Strict #-}&lt;/code&gt; worked right away, I didn't notice any bugs&lt;/li&gt;
&lt;li&gt;While replacing &lt;code&gt;IO&lt;/code&gt; functions with impure non-&lt;code&gt;IO&lt;/code&gt; versions, I replaced &lt;code&gt;withMVar&lt;/code&gt; with an impure version and doing so caused an infinite loop, so I reverted that one&lt;/li&gt;
&lt;li&gt;I found some dead code that was doing the equivalent of &lt;code&gt;x &amp;lt;- readMVar xVar&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Inlining variables into pure expressions helped align the code and prettify it&lt;/li&gt;
&lt;li&gt;Some control functions like &lt;code&gt;unlessM&lt;/code&gt; broke in unexpected ways&lt;/li&gt;
&lt;li&gt;All I could think about for the last 25% was "man, all these tricks with &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;/&lt;code&gt;&amp;lt;-&lt;/code&gt;/&lt;code&gt;&amp;lt;$&amp;gt;&lt;/code&gt;/&lt;code&gt;=&amp;lt;&amp;lt;&lt;/code&gt; to retain purity are completely unnecessary"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Value of enforcing purity in CodeWyng&lt;/strong&gt; The first version of the part of CodeWyng that is now written in Haskell was originally written in TypeScript, and I switched exclusively for Haskell's vastly superior runtime support for concurrency and the Haskell ecosystem's stream parsing libraries. I definitely didn't switch in order to prevent myself from inadvertently mixing pure/impure code, and if it were to be rewritten in some other language (TypeScript/Go/etc.), I wouldn't consider the loss of enforcement of purity a downside.&lt;/p&gt;

&lt;p&gt;Conversation on &lt;a href="https://funprog.srid.ca/haskell/effect-tracking-is-worthless.html" rel="noopener noreferrer"&gt;Zulip&lt;/a&gt;&lt;/p&gt;

</description>
      <category>haskell</category>
    </item>
    <item>
      <title>Haskell strings field guide</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Mon, 15 Oct 2018 00:00:00 +0000</pubDate>
      <link>https://forem.com/chrismwendt/haskell-strings-field-guide-2j24</link>
      <guid>https://forem.com/chrismwendt/haskell-strings-field-guide-2j24</guid>
      <description>&lt;p&gt;Here's how to convert between byte, string, and character types in Haskell:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;encodeUtf8&lt;/td&gt;
&lt;td&gt;Text -&amp;gt; ByteString&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unpack&lt;/td&gt;
&lt;td&gt;Text -&amp;gt; String&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decodeLatin1&lt;/td&gt;
&lt;td&gt;ByteString -&amp;gt; Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decodeUtf8With lenientDecode&lt;/td&gt;
&lt;td&gt;ByteString -&amp;gt; Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BC.unpack&lt;/td&gt;
&lt;td&gt;ByteString -&amp;gt; String&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8.toString&lt;/td&gt;
&lt;td&gt;ByteString -&amp;gt; String&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B.unpack&lt;/td&gt;
&lt;td&gt;ByteString -&amp;gt; [Word8]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTF8.fromString&lt;/td&gt;
&lt;td&gt;String -&amp;gt; ByteString&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pack&lt;/td&gt;
&lt;td&gt;String -&amp;gt; Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;w2c&lt;/td&gt;
&lt;td&gt;Word8 -&amp;gt; Char&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B.pack&lt;/td&gt;
&lt;td&gt;[Word8] -&amp;gt; ByteString&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c2w ❌ partial&lt;/td&gt;
&lt;td&gt;Char -&amp;gt; Word8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BC.pack ❌ partial&lt;/td&gt;
&lt;td&gt;String -&amp;gt; ByteString&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BSC.string ❌ stops streaming&lt;/td&gt;
&lt;td&gt;String -&amp;gt; BSC.ByteString&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note that Char is finite:&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;toEnum&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Char&lt;/span&gt;
&lt;span class="o"&gt;***&lt;/span&gt; &lt;span class="kt"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Prelude&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bad&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>haskell</category>
    </item>
    <item>
      <title>Chrome extension for highlighting variables on GitHub</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Fri, 30 Mar 2018 00:00:00 +0000</pubDate>
      <link>https://forem.com/chrismwendt/chrome-extension-for-highlighting-variables-on-github-c69</link>
      <guid>https://forem.com/chrismwendt/chrome-extension-for-highlighting-variables-on-github-c69</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/chrismwendt/github-highlight-variables" rel="noopener noreferrer"&gt;https://github.com/chrismwendt/github-highlight-variables&lt;/a&gt; highlights variables on hover on GitHub:&lt;/p&gt;

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

</description>
    </item>
    <item>
      <title>Auto import for Haskell in VS Code</title>
      <dc:creator>Chris Wendt</dc:creator>
      <pubDate>Sun, 25 Mar 2018 00:00:00 +0000</pubDate>
      <link>https://forem.com/chrismwendt/auto-import-for-haskell-in-vs-code-463b</link>
      <guid>https://forem.com/chrismwendt/auto-import-for-haskell-in-vs-code-463b</guid>
      <description>&lt;p&gt;Today, I hacked together auto imports for Haskell in VS Code: &lt;a href="https://github.com/chrismwendt/Auto-Import" rel="noopener noreferrer"&gt;https://github.com/chrismwendt/Auto-Import&lt;/a&gt; based on &lt;a href="https://github.com/soates/Auto-Import" rel="noopener noreferrer"&gt;https://github.com/soates/Auto-Import&lt;/a&gt; (which currently only supports TypeScript). The code is very rough, and there is a lot of room for improvement. Currently, it only searches for variables on Hoogle.&lt;/p&gt;

&lt;p&gt;It's cool to discover other packages and modules that export the same variable name, for example &lt;code&gt;concurrently&lt;/code&gt; appears in &lt;code&gt;async&lt;/code&gt; (what I would expect), but also appears in other libraries that generalize &lt;code&gt;async&lt;/code&gt;'s types:&lt;/p&gt;

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

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>tooling</category>
      <category>vscode</category>
    </item>
  </channel>
</rss>
