<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://b.s5.pm/feed.xml" rel="self" type="application/atom+xml" /><link href="https://b.s5.pm/" rel="alternate" type="text/html" /><updated>2024-05-19T02:06:07+00:00</updated><id>https://b.s5.pm/feed.xml</id><title type="html">blog</title><subtitle>[insert catchy description here]</subtitle><entry><title type="html">A No-Bullshit Guide to Setting up Windows 10</title><link href="https://b.s5.pm/os/2021/08/28/windows-setup.html" rel="alternate" type="text/html" title="A No-Bullshit Guide to Setting up Windows 10" /><published>2021-08-28T16:28:59+00:00</published><updated>2021-08-28T16:28:59+00:00</updated><id>https://b.s5.pm/os/2021/08/28/windows-setup</id><content type="html" xml:base="https://b.s5.pm/os/2021/08/28/windows-setup.html"><![CDATA[<h2 id="intended-audience">Intended Audience</h2>

<p>The instructions for this guide are geared towards users who are comfortable
with Linux and want a useful setup, or dual-boot a Linux OS and want to share
data with it. For the latter, this guide uses <code class="language-plaintext highlighter-rouge">F:\</code> to refer to the shared
drive. If you do not need a shared drive, you can mostly ignore those
instructions.</p>

<p>Note: Previous iterations of this blog post included instructions on how to
move <code class="language-plaintext highlighter-rouge">C:\Users</code> to a different drive. This has since been confirmed by
Microsoft Support to create an invalid system configuration, and from
experience will break a lot of things.</p>

<h3 id="extra-goals">Extra Goals</h3>

<p>This guide also goes through the process of setting up a web browser, a
terminal, and a JVM manager.</p>

<h3 id="non-goals">Non-Goals</h3>

<p>This guide will not cover custom themes or any sort of modifications to system
internals. All modifications done in this guide are achieved through use of
registry modifications or official Windows tools.</p>

<h3 id="issues-or-questions">Issues or Questions</h3>

<p>The footer of this blog contains a link to my GitHub. Open an issue on the
<code class="language-plaintext highlighter-rouge">blog</code> repository there to report issues or to ask questions.</p>

<h2 id="prerequisites-and-terms">Prerequisites and Terms</h2>

<p>This is a list of terms, including drive names. These names may be different
for your setup. <strong>I will not be mentioning when you should replace a drive name
with your own. Look out for drive names in commands and variable strings.</strong></p>

<dl>
  <dt><strong>(o)</strong></dt>
  <dd>This marks a section that is mainly opinionated. It doesn’t align with the
goals of the guide, but might be helpful for people who don’t want to manage
something when it comes up down the line.</dd>
  <dt><strong>The Windows Machine</strong></dt>
  <dd>A fresh Windows 10 installation</dd>
  <dt><strong><code class="language-plaintext highlighter-rouge">C:\</code></strong></dt>
  <dd>The boot drive/partition of Windows 10. This is the smaller/faster of the two
drives in the two-drive setup.</dd>
  <dt><strong><code class="language-plaintext highlighter-rouge">F:\</code></strong></dt>
  <dd>The data and programs partition. When dual-booting Linux, this is your
“shared with Linux” drive. In this case, it must be formatted to a filesystem
that can be used on both Windows and Linux such as
<a href="https://github.com/maharmstone/btrfs">BTRFS</a>. <strong>Do your own research.</strong> I am
not to be trusted with filesystems.</dd>
  <dt><strong>Special Directories</strong></dt>
  <dd>These are directories that can be relocated per-user, such as Documents,
Downloads, Desktop, Pictures, and Videos.</dd>
  <dt><strong><code class="language-plaintext highlighter-rouge">_EXAMPLE_TEXT_</code></strong></dt>
  <dd>An all-caps identifier surrounded by underscores is meant to be a
user-defined variable. It <em>can</em> be an environment variable if desired, but it
is not required to be one. If something is required to be an environment
variable, it should be explicitly mentioned.</dd>
</dl>

<h2 id="first-boot">First Boot</h2>

<h3 id="relocating-special-directories">Relocating Special Directories</h3>

<p><em>You can skip this section if not using <code class="language-plaintext highlighter-rouge">F:\</code>.</em></p>

<p>The special directories in your user profile can be relocated to more helpful
locations, especially if you’re sharing data with a Linux installation. For
this, right click on a special directory and go to “Properties”. One of the
properties tabs should say “Location”. Here you can choose the new location of
the special folder, pointing it to <code class="language-plaintext highlighter-rouge">F:\Documents</code> or <code class="language-plaintext highlighter-rouge">F:\Downloads</code>.</p>

<h3 id="removing-the-max_path-limit">Removing the <code class="language-plaintext highlighter-rouge">MAX_PATH</code> Limit</h3>

<p>Open an administrator PowerShell, and run</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">New-ItemProperty</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="s2">"HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem"</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"LongPathsEnabled"</span><span class="w"> </span><span class="nt">-Value</span><span class="w"> </span><span class="nx">1</span><span class="w"> </span><span class="nt">-PropertyType</span><span class="w"> </span><span class="nx">DWORD</span><span class="w"> </span><span class="nt">-Force</span></code></pre></figure>

<h3 id="using-utf-8-instead-of-latin-1-by-default">Using UTF-8 Instead of Latin-1 by Default</h3>

<p>Open Control Panel. Select “Change date, time, or number formats” under “Clock
and Region”. In the new “Region” window that opens, select the “Administrative”
tab. Click the button that says “Change system locale…” and enable the box
that says “Use Unicode UTF-8 for worldwide language support”.</p>

<h2 id="second-boot">Second Boot</h2>

<h3 id="install-winget">Install WinGet</h3>

<p>WinGet should already be installed, but check
<a href="https://docs.microsoft.com/en-us/windows/package-manager/winget/#install-winget">Microsoft’s Install winget guide</a>
to make sure.</p>

<p>Note that when running <code class="language-plaintext highlighter-rouge">winget install</code>, you have the option of specifying a
<code class="language-plaintext highlighter-rouge">-l</code>/<code class="language-plaintext highlighter-rouge">--location</code> flag to choose the install directory. Only some installers
will support this, however.</p>

<h3 id="install-the-latest-powershell-o">Install the Latest PowerShell (o)</h3>

<p>PowerShell Core can be installed via <code class="language-plaintext highlighter-rouge">winget install Microsoft.PowerShell</code>.</p>

<h3 id="install-gsudo">Install <code class="language-plaintext highlighter-rouge">gsudo</code></h3>

<p><code class="language-plaintext highlighter-rouge">gsudo</code> is the Windows equivalent of Linux’s <code class="language-plaintext highlighter-rouge">sudo</code>. It lets you elevate
commands without starting a new command prompt. To install it, run
<code class="language-plaintext highlighter-rouge">winget install gerardog.gsudo</code>.</p>

<h3 id="install-a-better-terminal-o">Install a Better Terminal (o)</h3>

<p>Windows Terminal, Hyper, Cmder, etc. all provide some nicer abstractions over
the base <code class="language-plaintext highlighter-rouge">cmd</code> and <code class="language-plaintext highlighter-rouge">pwsh</code>. If you have a favorite terminal, you should install
it now.</p>

<p>Windows Terminal can be installed via
<code class="language-plaintext highlighter-rouge">winget install Microsoft.WindowsTerminal</code>.</p>

<h3 id="install-a-browser-o">Install a Browser (o)</h3>

<p>If you prefer something other than Microsoft Edge, now would be the time to
install it.</p>

<ul>
  <li>Google Chrome can be installed via <code class="language-plaintext highlighter-rouge">winget install Google.Chrome</code>.</li>
  <li>Firefox can be installed via <code class="language-plaintext highlighter-rouge">winget install Mozilla.Firefox</code>.</li>
</ul>

<h3 id="install-wincompose-o">Install WinCompose (o)</h3>

<p>WinCompose allows you to enter special characters without the hassle of
remembering numeric Alt-codes. It’s available at
<a href="https://github.com/samhocevar/wincompose">https://github.com/samhocevar/wincompose</a>.</p>

<p>WinCompose can be installed via <code class="language-plaintext highlighter-rouge">winget install SamHocevar.WinCompose</code>.</p>

<h3 id="install-mactype-o">Install MacType (o)</h3>

<p>MacType offers an alternative fond renderer that makes certain text much more
smooth and crisp. It’s available at
<a href="https://github.com/snowie2000/mactype">https://github.com/snowie2000/mactype</a>.</p>

<p>MacType can be installed via <code class="language-plaintext highlighter-rouge">winget install MacType.MacType</code>.</p>

<p>If you use a Chrome-based browser, you will want to
<a href="https://github.com/snowie2000/mactype/wiki/Google-Chrome#policy-thanks-to-kcohar">disable the renderer code integrity policy</a>.</p>

<p>You will want to add exclusions for Source games, as the Source console will be
effectively unreadable under MacType. When you have the game open, open the
MacType Wizard, go to its Process Manager, find the game, and check both
options under the right-click menu. Both “Exclude this process” and “Don’t
replace fonts for this process” should be checked. Relaunching the game should
make the console readable again.</p>

<h3 id="sycnex-debloater">Sycnex Debloater</h3>

<p>Follow the instructions at
<a href="https://github.com/Sycnex/Windows10Debloater">https://github.com/Sycnex/Windows10Debloater</a>
to launch <code class="language-plaintext highlighter-rouge">Windows10DebloaterGUI</code>.</p>

<p>Run these options:</p>

<dl>
  <dt><strong>Remove All Bloatware</strong></dt>
  <dd>Disables any sort of advertising. Removes unimportant applications.</dd>
  <dt><strong>Disable Cortana</strong></dt>
  <dd>Does what it says on the box.</dd>
  <dt><strong>Uninstall OneDrive</strong></dt>
  <dd>Completely removes OneDrive support from Explorer. Will create a backup of
your OneDrive files on your Desktop. If you get notifications about <code class="language-plaintext highlighter-rouge">pwsh</code>
downloading a bunch of files, then you can permanently block the app to make
the Debloater think there are no files left to download.</dd>
</dl>

<h3 id="shutup10">Shutup10</h3>

<p>This is a tool that fine-tunes the debloating done by Sycnex Debloater. It’s
available to download from
<a href="https://www.oo-software.com/en/shutup10">https://www.oo-software.com/en/shutup10</a>.</p>

<p>You will want to choose “Actions” and then “Apply only recommended settings”.</p>

<p>Note: This will disable clipboard history. If you’re like me and want Clipboard
History, scroll down to “Activity History and Clipboard” and disable (make the
switches red) the two options that mention “storage of clipboard history”.</p>

<p>“File” and “Exit” Shutup10. It will prompt you to reboot.</p>

<h2 id="extra-utilities">Extra Utilities</h2>

<h3 id="git">Git</h3>

<p>You most likely want to install Git on Windows without installing the entirety
of Vim and Git Bash. Git for Windows provides a distribution called “MinGit”
that does exactly this.</p>

<p>Navigate to the latest release of Git for Windows:
<a href="https://github.com/git-for-windows/git/releases/latest">https://github.com/git-for-windows/git/releases/latest</a>.
Download the latest version of <code class="language-plaintext highlighter-rouge">MinGit</code> that does <strong>not</strong> mention <code class="language-plaintext highlighter-rouge">busybox</code>,
and is <code class="language-plaintext highlighter-rouge">64-bit</code>. (At the time of writing, this is <code class="language-plaintext highlighter-rouge">MinGit-2.36.1-64-bit.zip</code>.)</p>

<p>If you want Git to be installed for all users, you can extract it to
<code class="language-plaintext highlighter-rouge">C:\Program Files\</code>, such that <code class="language-plaintext highlighter-rouge">git</code> can be found at
<code class="language-plaintext highlighter-rouge">C:\Program Files\Git\cmd\git.exe</code>. Make sure to edit
<code class="language-plaintext highlighter-rouge">C:\Program Files\Git\etc\gitconfig</code> to not be self-referential.</p>

<p>If not, extract <code class="language-plaintext highlighter-rouge">Git</code> to your
<code class="language-plaintext highlighter-rouge">%LocalAppData%</code>, such that <code class="language-plaintext highlighter-rouge">git</code> can be found at
<code class="language-plaintext highlighter-rouge">%LocalAppData%\Git\cmd\git.exe</code>.</p>

<p>Then add the full path to the <code class="language-plaintext highlighter-rouge">cmd</code> folder to
your User <code class="language-plaintext highlighter-rouge">Path</code> environment variable.</p>

<h3 id="haskell-ghcup">Haskell (GHCUp)</h3>

<p>Download
<a href="https://downloads.haskell.org/~ghcup/x86_64-mingw64-ghcup.exe">the latest version of the GHCUp installer</a>
to somewhere on your <code class="language-plaintext highlighter-rouge">PATH</code>. Recommended is to add a new directory to your
<code class="language-plaintext highlighter-rouge">PATH</code>, such as <code class="language-plaintext highlighter-rouge">F:\Win\ghcup</code> or <code class="language-plaintext highlighter-rouge">%LocalAppData%\ghcup</code>.</p>

<p>Set the environment variable <code class="language-plaintext highlighter-rouge">GHCUP_INSTALL_BASE_PREFIX</code> to <code class="language-plaintext highlighter-rouge">F:\Win\ghcup</code> or
<code class="language-plaintext highlighter-rouge">%LocalAppData%\ghcup</code> respectively. Then, run <code class="language-plaintext highlighter-rouge">ghcup install ghc</code>. Afterwards,
there should be a new <code class="language-plaintext highlighter-rouge">%GHCUP_INSTALL_BASE_PREFIX%\ghcup\bin</code> folder that you
should add to your <code class="language-plaintext highlighter-rouge">Path</code>.</p>

<h3 id="java--scala">Java + Scala</h3>

<p>If you will also need Scala tools, <a href="https://get-coursier.io/">Coursier</a> ships
with a Java manager. Instead of following the instructions for using <code class="language-plaintext highlighter-rouge">cmd</code>, you
can run this command for PowerShell:</p>

<figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="n">Invoke-WebRequest</span><span class="w"> </span><span class="nt">-Uri</span><span class="w"> </span><span class="s2">"https://github.com/coursier/launchers/raw/master/cs-x86_64-pc-win32.zip"</span><span class="w"> </span><span class="nt">-OutFile</span><span class="w"> </span><span class="s2">"cs-x86_64-pc-win32.zip"</span><span class="w">
</span><span class="n">Expand-Archive</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="s2">"cs-x86_64-pc-win32.zip"</span><span class="w">
</span><span class="n">Rename-Item</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="s2">"cs-x86_64-pc-win32.exe"</span><span class="w"> </span><span class="nt">-NewName</span><span class="w"> </span><span class="s2">"cs.exe"</span><span class="w">
</span><span class="n">Remove-Item</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="s2">"cs-x86_64-pc-win32.zip"</span></code></pre></figure>

<p>Before running <code class="language-plaintext highlighter-rouge">.\cs.exe</code>, make sure to set the <code class="language-plaintext highlighter-rouge">COURSIER_CACHE</code> environment
variable to the location of your choice: Coursier has a bug right now where it
will fail to find the default cache location
(<a href="https://github.com/coursier/coursier/issues/2031">#2031</a>,
<a href="https://github.com/coursier/coursier/issues/2118">#2118</a>). When using <code class="language-plaintext highlighter-rouge">F:\</code>,
you may want something like <code class="language-plaintext highlighter-rouge">F:\Win\Program Files\Coursier\Cache\v1</code>. If you’re
not sure, refer to the
<a href="https://get-coursier.io/docs/cache#default-location">the Coursier cache documentation</a>.</p>

<p>Make sure to set <code class="language-plaintext highlighter-rouge">COURSIER_BIN_DIR</code> and <code class="language-plaintext highlighter-rouge">COURSIER_JVM_CACHE</code> as well. If you
set <code class="language-plaintext highlighter-rouge">COURSIER_CACHE</code> to <code class="language-plaintext highlighter-rouge">_COURSIER_ROOT_\Cache\v1</code> (in the above example,
<code class="language-plaintext highlighter-rouge">_COURSIER_ROOT_</code> would be <code class="language-plaintext highlighter-rouge">F:\Win\Program Files\Coursier</code>), then I recommend
<code class="language-plaintext highlighter-rouge">COURSIER_BIN_DIR</code> to be <code class="language-plaintext highlighter-rouge">_COURSIER_ROOT_\bin</code> and <code class="language-plaintext highlighter-rouge">COURSIER_JVM_CACHE</code> to be
<code class="language-plaintext highlighter-rouge">_COURSIER_ROOT_\Cache\jvm</code>.</p>

<p>In full, if your <code class="language-plaintext highlighter-rouge">_COURSIER_ROOT_</code> is <code class="language-plaintext highlighter-rouge">F:\Win\Program Files\Coursier</code>, then</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">COURSIER_CACHE</code>: <code class="language-plaintext highlighter-rouge">F:\Win\Program Files\Coursier\Cache\v1</code></li>
  <li><code class="language-plaintext highlighter-rouge">COURSIER_BIN_DIR</code>: <code class="language-plaintext highlighter-rouge">F:\Win\Program Files\Coursier\bin</code></li>
  <li><code class="language-plaintext highlighter-rouge">COURSIER_JVM_CACHE</code>: <code class="language-plaintext highlighter-rouge">F:\Win\Program Files\Coursier\Cache\jvm</code></li>
</ul>

<p>Try to run <code class="language-plaintext highlighter-rouge">.\cs.exe</code>. If you get a VCRUNTIME140.DLL missing error, or nothing
happens, download the <code class="language-plaintext highlighter-rouge">vc_redist.x64.exe</code> from
<a href="https://www.microsoft.com/en-us/download/details.aspx?id=52685">https://www.microsoft.com/en-us/download/details.aspx?id=52685</a>.</p>

<p>If running <code class="language-plaintext highlighter-rouge">.\cs.exe</code> succeeds, you can run <code class="language-plaintext highlighter-rouge">.\cs.exe setup</code>, which will
install Coursier and basic Scala development tools to <code class="language-plaintext highlighter-rouge">COURSIER_BIN_DIR</code>. If
this succeeds, you will be able to remove the downloaded <code class="language-plaintext highlighter-rouge">cs.exe</code> and run the
<code class="language-plaintext highlighter-rouge">cs</code> that exists on your <code class="language-plaintext highlighter-rouge">PATH</code>.</p>

<p><code class="language-plaintext highlighter-rouge">cs java --available</code> will list JDKs available for installation, and
<code class="language-plaintext highlighter-rouge">cs java --jvm _JDK_NAME_ --setup</code>, with <code class="language-plaintext highlighter-rouge">_JDK_NAME_</code> set to an entry of that
list, will download that specific JDK and set it to be your default with
<code class="language-plaintext highlighter-rouge">JAVA_HOME</code>.</p>

<h3 id="java-no-scala">Java (no Scala)</h3>

<p>Install <a href="https://github.com/shyiko/jabba">Jabba</a>.</p>

<h3 id="javascript-node--yarn">JavaScript (Node + Yarn)</h3>

<p>Install <a href="https://github.com/coreybutler/nvm-windows">nvm-windows</a>. Once you
have a <code class="language-plaintext highlighter-rouge">node</code> installed, install Yarn with <code class="language-plaintext highlighter-rouge">gsudo npm install --global yarn</code>.</p>

<p>Yarn by default will dump things in <code class="language-plaintext highlighter-rouge">%LocalAppData%\Yarn\</code>. If you use <code class="language-plaintext highlighter-rouge">F:\</code>,
you most likely don’t want this. Configure Yarn to use a more appropriate
directory:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yarn config set prefix _YARN_ROOT_
yarn config set cache-folder _YARN_ROOT_\Cache
yarn config set global-folder _YARN_ROOT_\Data\global
</code></pre></div></div>

<p>Replacing all instances of <code class="language-plaintext highlighter-rouge">_YARN_ROOT_</code> with your desired Yarn root, for
example <code class="language-plaintext highlighter-rouge">F:\Win\Yarn</code>. You do not have to create a <code class="language-plaintext highlighter-rouge">YARN_ROOT</code> environment
variable, although you can if you would like.</p>

<p>Next, add the output of <code class="language-plaintext highlighter-rouge">yarn global bin</code> to your <code class="language-plaintext highlighter-rouge">Path</code>. For example, if your
output looked like</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PS&gt; yarn global bin
F:\Win\Yarn\bin
</code></pre></div></div>

<p>You would add <code class="language-plaintext highlighter-rouge">F:\Win\Yarn\bin</code> to your <code class="language-plaintext highlighter-rouge">Path</code>. If you created a <code class="language-plaintext highlighter-rouge">YARN_ROOT</code>
environment variable, you can substitute it here.</p>

<p>Note that there is currently a bug in Yarn where it will think it’s running on
a Linux machine when generating wrappers
(<a href="https://github.com/yarnpkg/yarn/issues/6295">#6295</a>). You will need to edit
any of the <code class="language-plaintext highlighter-rouge">.cmd</code> files of installed binaries. For example, with
<code class="language-plaintext highlighter-rouge">yarn global add vsce</code>, you would want to edit the <code class="language-plaintext highlighter-rouge">vsce.cmd</code> found in the
<code class="language-plaintext highlighter-rouge">yarn global bin</code> folder from</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@IF EXIST "%~dp0\/bin/sh" (
  "%~dp0\/bin/sh"  "%~dp0\..\Data\global\node_modules\.bin\vsce" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  /bin/sh  "%~dp0\..\Data\global\node_modules\.bin\vsce" %*
)
</code></pre></div></div>

<p>to</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@IF EXIST "%~dp0\cmd.exe" (
  "%~dp0\cmd.exe"  "%~dp0\..\Data\global\node_modules\.bin\vsce.cmd" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  cmd /c "%~dp0\..\Data\global\node_modules\.bin\vsce.cmd" %*
)
</code></pre></div></div>

<h3 id="rust">Rust</h3>

<p>Install the
<a href="https://visualstudio.microsoft.com/visual-cpp-build-tools/">Visual C++ Build Tools</a>.
The default options will work. If you use <code class="language-plaintext highlighter-rouge">F:\</code>, you will want to change the
directories listed in the “Install Locations” tab.</p>

<p>Download <code class="language-plaintext highlighter-rouge">rustup-init.exe</code> from <a href="https://rustup.rs/">the rustup website</a>. If
you use <code class="language-plaintext highlighter-rouge">F:\</code>, make sure to set the <code class="language-plaintext highlighter-rouge">RUSTUP_HOME</code> and <code class="language-plaintext highlighter-rouge">CARGO_HOME</code> environment
variables to your desired locations. If you’re not sure, refer to
<a href="https://rust-lang.github.io/rustup/installation/index.html#choosing-where-to-install">the Rust documentation</a>.</p>

<p>Then, run <code class="language-plaintext highlighter-rouge">rustup-init.exe</code>.</p>]]></content><author><name></name></author><category term="os" /><category term="windows" /><summary type="html"><![CDATA[Intended Audience The instructions for this guide are geared towards users who are comfortable with Linux and want a useful setup, or dual-boot a Linux OS and want to share data with it. For the latter, this guide uses F:\ to refer to the shared drive. If you do not need a shared drive, you can mostly ignore those instructions. Note: Previous iterations of this blog post included instructions on how to move C:\Users to a different drive. This has since been confirmed by Microsoft Support to create an invalid system configuration, and from experience will break a lot of things. Extra Goals This guide also goes through the process of setting up a web browser, a terminal, and a JVM manager. Non-Goals This guide will not cover custom themes or any sort of modifications to system internals. All modifications done in this guide are achieved through use of registry modifications or official Windows tools. Issues or Questions The footer of this blog contains a link to my GitHub. Open an issue on the blog repository there to report issues or to ask questions. Prerequisites and Terms This is a list of terms, including drive names. These names may be different for your setup. I will not be mentioning when you should replace a drive name with your own. Look out for drive names in commands and variable strings. (o) This marks a section that is mainly opinionated. It doesn’t align with the goals of the guide, but might be helpful for people who don’t want to manage something when it comes up down the line. The Windows Machine A fresh Windows 10 installation C:\ The boot drive/partition of Windows 10. This is the smaller/faster of the two drives in the two-drive setup. F:\ The data and programs partition. When dual-booting Linux, this is your “shared with Linux” drive. In this case, it must be formatted to a filesystem that can be used on both Windows and Linux such as BTRFS. Do your own research. I am not to be trusted with filesystems. Special Directories These are directories that can be relocated per-user, such as Documents, Downloads, Desktop, Pictures, and Videos. _EXAMPLE_TEXT_ An all-caps identifier surrounded by underscores is meant to be a user-defined variable. It can be an environment variable if desired, but it is not required to be one. If something is required to be an environment variable, it should be explicitly mentioned. First Boot Relocating Special Directories You can skip this section if not using F:\. The special directories in your user profile can be relocated to more helpful locations, especially if you’re sharing data with a Linux installation. For this, right click on a special directory and go to “Properties”. One of the properties tabs should say “Location”. Here you can choose the new location of the special folder, pointing it to F:\Documents or F:\Downloads. Removing the MAX_PATH Limit Open an administrator PowerShell, and run New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force Using UTF-8 Instead of Latin-1 by Default Open Control Panel. Select “Change date, time, or number formats” under “Clock and Region”. In the new “Region” window that opens, select the “Administrative” tab. Click the button that says “Change system locale…” and enable the box that says “Use Unicode UTF-8 for worldwide language support”. Second Boot Install WinGet WinGet should already be installed, but check Microsoft’s Install winget guide to make sure. Note that when running winget install, you have the option of specifying a -l/--location flag to choose the install directory. Only some installers will support this, however. Install the Latest PowerShell (o) PowerShell Core can be installed via winget install Microsoft.PowerShell. Install gsudo gsudo is the Windows equivalent of Linux’s sudo. It lets you elevate commands without starting a new command prompt. To install it, run winget install gerardog.gsudo. Install a Better Terminal (o) Windows Terminal, Hyper, Cmder, etc. all provide some nicer abstractions over the base cmd and pwsh. If you have a favorite terminal, you should install it now. Windows Terminal can be installed via winget install Microsoft.WindowsTerminal. Install a Browser (o) If you prefer something other than Microsoft Edge, now would be the time to install it. Google Chrome can be installed via winget install Google.Chrome. Firefox can be installed via winget install Mozilla.Firefox. Install WinCompose (o) WinCompose allows you to enter special characters without the hassle of remembering numeric Alt-codes. It’s available at https://github.com/samhocevar/wincompose. WinCompose can be installed via winget install SamHocevar.WinCompose. Install MacType (o) MacType offers an alternative fond renderer that makes certain text much more smooth and crisp. It’s available at https://github.com/snowie2000/mactype. MacType can be installed via winget install MacType.MacType. If you use a Chrome-based browser, you will want to disable the renderer code integrity policy. You will want to add exclusions for Source games, as the Source console will be effectively unreadable under MacType. When you have the game open, open the MacType Wizard, go to its Process Manager, find the game, and check both options under the right-click menu. Both “Exclude this process” and “Don’t replace fonts for this process” should be checked. Relaunching the game should make the console readable again. Sycnex Debloater Follow the instructions at https://github.com/Sycnex/Windows10Debloater to launch Windows10DebloaterGUI. Run these options: Remove All Bloatware Disables any sort of advertising. Removes unimportant applications. Disable Cortana Does what it says on the box. Uninstall OneDrive Completely removes OneDrive support from Explorer. Will create a backup of your OneDrive files on your Desktop. If you get notifications about pwsh downloading a bunch of files, then you can permanently block the app to make the Debloater think there are no files left to download. Shutup10 This is a tool that fine-tunes the debloating done by Sycnex Debloater. It’s available to download from https://www.oo-software.com/en/shutup10. You will want to choose “Actions” and then “Apply only recommended settings”. Note: This will disable clipboard history. If you’re like me and want Clipboard History, scroll down to “Activity History and Clipboard” and disable (make the switches red) the two options that mention “storage of clipboard history”. “File” and “Exit” Shutup10. It will prompt you to reboot. Extra Utilities Git You most likely want to install Git on Windows without installing the entirety of Vim and Git Bash. Git for Windows provides a distribution called “MinGit” that does exactly this. Navigate to the latest release of Git for Windows: https://github.com/git-for-windows/git/releases/latest. Download the latest version of MinGit that does not mention busybox, and is 64-bit. (At the time of writing, this is MinGit-2.36.1-64-bit.zip.) If you want Git to be installed for all users, you can extract it to C:\Program Files\, such that git can be found at C:\Program Files\Git\cmd\git.exe. Make sure to edit C:\Program Files\Git\etc\gitconfig to not be self-referential. If not, extract Git to your %LocalAppData%, such that git can be found at %LocalAppData%\Git\cmd\git.exe. Then add the full path to the cmd folder to your User Path environment variable. Haskell (GHCUp) Download the latest version of the GHCUp installer to somewhere on your PATH. Recommended is to add a new directory to your PATH, such as F:\Win\ghcup or %LocalAppData%\ghcup. Set the environment variable GHCUP_INSTALL_BASE_PREFIX to F:\Win\ghcup or %LocalAppData%\ghcup respectively. Then, run ghcup install ghc. Afterwards, there should be a new %GHCUP_INSTALL_BASE_PREFIX%\ghcup\bin folder that you should add to your Path. Java + Scala If you will also need Scala tools, Coursier ships with a Java manager. Instead of following the instructions for using cmd, you can run this command for PowerShell: Invoke-WebRequest -Uri "https://github.com/coursier/launchers/raw/master/cs-x86_64-pc-win32.zip" -OutFile "cs-x86_64-pc-win32.zip" Expand-Archive -Path "cs-x86_64-pc-win32.zip" Rename-Item -Path "cs-x86_64-pc-win32.exe" -NewName "cs.exe" Remove-Item -Path "cs-x86_64-pc-win32.zip" Before running .\cs.exe, make sure to set the COURSIER_CACHE environment variable to the location of your choice: Coursier has a bug right now where it will fail to find the default cache location (#2031, #2118). When using F:\, you may want something like F:\Win\Program Files\Coursier\Cache\v1. If you’re not sure, refer to the the Coursier cache documentation. Make sure to set COURSIER_BIN_DIR and COURSIER_JVM_CACHE as well. If you set COURSIER_CACHE to _COURSIER_ROOT_\Cache\v1 (in the above example, _COURSIER_ROOT_ would be F:\Win\Program Files\Coursier), then I recommend COURSIER_BIN_DIR to be _COURSIER_ROOT_\bin and COURSIER_JVM_CACHE to be _COURSIER_ROOT_\Cache\jvm. In full, if your _COURSIER_ROOT_ is F:\Win\Program Files\Coursier, then COURSIER_CACHE: F:\Win\Program Files\Coursier\Cache\v1 COURSIER_BIN_DIR: F:\Win\Program Files\Coursier\bin COURSIER_JVM_CACHE: F:\Win\Program Files\Coursier\Cache\jvm Try to run .\cs.exe. If you get a VCRUNTIME140.DLL missing error, or nothing happens, download the vc_redist.x64.exe from https://www.microsoft.com/en-us/download/details.aspx?id=52685. If running .\cs.exe succeeds, you can run .\cs.exe setup, which will install Coursier and basic Scala development tools to COURSIER_BIN_DIR. If this succeeds, you will be able to remove the downloaded cs.exe and run the cs that exists on your PATH. cs java --available will list JDKs available for installation, and cs java --jvm _JDK_NAME_ --setup, with _JDK_NAME_ set to an entry of that list, will download that specific JDK and set it to be your default with JAVA_HOME. Java (no Scala) Install Jabba. JavaScript (Node + Yarn) Install nvm-windows. Once you have a node installed, install Yarn with gsudo npm install --global yarn. Yarn by default will dump things in %LocalAppData%\Yarn\. If you use F:\, you most likely don’t want this. Configure Yarn to use a more appropriate directory: yarn config set prefix _YARN_ROOT_ yarn config set cache-folder _YARN_ROOT_\Cache yarn config set global-folder _YARN_ROOT_\Data\global Replacing all instances of _YARN_ROOT_ with your desired Yarn root, for example F:\Win\Yarn. You do not have to create a YARN_ROOT environment variable, although you can if you would like. Next, add the output of yarn global bin to your Path. For example, if your output looked like PS&gt; yarn global bin F:\Win\Yarn\bin You would add F:\Win\Yarn\bin to your Path. If you created a YARN_ROOT environment variable, you can substitute it here. Note that there is currently a bug in Yarn where it will think it’s running on a Linux machine when generating wrappers (#6295). You will need to edit any of the .cmd files of installed binaries. For example, with yarn global add vsce, you would want to edit the vsce.cmd found in the yarn global bin folder from @IF EXIST "%~dp0\/bin/sh" ( "%~dp0\/bin/sh" "%~dp0\..\Data\global\node_modules\.bin\vsce" %* ) ELSE ( @SETLOCAL @SET PATHEXT=%PATHEXT:;.JS;=;% /bin/sh "%~dp0\..\Data\global\node_modules\.bin\vsce" %* ) to @IF EXIST "%~dp0\cmd.exe" ( "%~dp0\cmd.exe" "%~dp0\..\Data\global\node_modules\.bin\vsce.cmd" %* ) ELSE ( @SETLOCAL @SET PATHEXT=%PATHEXT:;.JS;=;% cmd /c "%~dp0\..\Data\global\node_modules\.bin\vsce.cmd" %* ) Rust Install the Visual C++ Build Tools. The default options will work. If you use F:\, you will want to change the directories listed in the “Install Locations” tab. Download rustup-init.exe from the rustup website. If you use F:\, make sure to set the RUSTUP_HOME and CARGO_HOME environment variables to your desired locations. If you’re not sure, refer to the Rust documentation. Then, run rustup-init.exe.]]></summary></entry><entry><title type="html">The Evolution of a DSL to Tagless Final</title><link href="https://b.s5.pm/programming/2020/03/01/final-tagless.html" rel="alternate" type="text/html" title="The Evolution of a DSL to Tagless Final" /><published>2020-03-01T02:40:23+00:00</published><updated>2020-03-01T02:40:23+00:00</updated><id>https://b.s5.pm/programming/2020/03/01/final-tagless</id><content type="html" xml:base="https://b.s5.pm/programming/2020/03/01/final-tagless.html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>I wrote this article to help others who don’t understand why Final Tagless is
important, and to guide in how to reason about a DSL in a way that makes Final
Tagless an appropriate solution. I was stuck in a situation when, once I was
learning about Final Tagless, I didn’t understand how it should be applied to
my projects or if it even could. This article mainly aligns with the process I
went through to solve these issues.</p>

<p>Side notes are aimed toward readers familiar with <a href="https://typelevel.org/cats">cats</a> and programming
with higher kinded types.</p>

<p>If you are more familiar with “Final Tagless Algebra” than “Final Tagless
Language,” note that I will be more commonly using the latter, even though the
former is more correct.</p>

<p>If you already understand what a DSL is and how to write programs using monad
composition, you can skip to
<a href="#an-overview-of-final-tagless">An Overview of Final Tagless</a>.</p>

<h3 id="what-is-a-dsl">What is a DSL?</h3>

<p>DSL stands for “Domain-Specific Language.” While the literal definition
encompasses languages like PostScript, made for printing, and Verilog, made for
hardware, a more general and commonly used definition includes any set of
operations (Turing-completeness disregarded).</p>

<p>A common example of a DSL is a calculator:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">object</span> <span class="nc">Calculator</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">add</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
  <span class="k">def</span> <span class="nf">subtract</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">a</span> <span class="o">-</span> <span class="n">b</span>

<span class="o">}</span></code></pre></figure>

<p>We can very easily perform arbitrary operations with our calculator:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="nv">Calculator</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="mi">2</span><span class="o">)</span>
<span class="c1">// =&gt; 3</span>

<span class="nv">Calculator</span><span class="o">.</span><span class="py">subtract</span><span class="o">(</span>
  <span class="nv">Calculator</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">3</span><span class="o">),</span>
  <span class="mi">4</span>
<span class="o">)</span>
<span class="c1">// =&gt; 1</span></code></pre></figure>

<h3 id="monads">Monads</h3>

<p>A Monad is any sort of data structure or container that defines these two
operations:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">trait</span> <span class="nc">Monad</span><span class="o">[</span><span class="kt">M</span><span class="o">[</span><span class="k">_</span><span class="o">]]</span> <span class="o">{</span>
  <span class="k">def</span> <span class="nf">pure</span><span class="o">[</span><span class="kt">A</span><span class="o">](</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span>
  <span class="k">def</span> <span class="nf">flatMap</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">ma</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">A</span><span class="o">])(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="n">M</span><span class="o">[</span><span class="kt">B</span><span class="o">])</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span>
<span class="o">}</span></code></pre></figure>

<p>Various other behaviors can be derived from these definitions:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">def</span> <span class="nf">map</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">ma</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">A</span><span class="o">])(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="n">B</span><span class="o">)</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span> <span class="k">=</span> <span class="nf">flatMap</span><span class="o">(</span><span class="n">ma</span><span class="o">)(</span><span class="n">pure</span> <span class="n">compose</span> <span class="n">f</span><span class="o">)</span>
<span class="k">def</span> <span class="nf">flatten</span><span class="o">[</span><span class="kt">A</span><span class="o">](</span><span class="n">mma</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">M</span><span class="o">[</span><span class="kt">A</span><span class="o">]])</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">=</span> <span class="nf">flatMap</span><span class="o">(</span><span class="n">mma</span><span class="o">)(</span><span class="n">identity</span><span class="o">)</span>
<span class="k">def</span> <span class="nf">ap</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">ma</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">A</span><span class="o">])(</span><span class="n">mf</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">A</span> <span class="k">=&gt;</span> <span class="kt">B</span><span class="o">])</span><span class="k">:</span> <span class="kt">M</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span> <span class="k">=</span> <span class="nf">flatMap</span><span class="o">(</span><span class="n">mf</span><span class="o">)(</span><span class="nf">map</span><span class="o">(</span><span class="n">ma</span><span class="o">))</span></code></pre></figure>

<p>For example, <code class="language-plaintext highlighter-rouge">List</code> is a Monad:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">def</span> <span class="nf">pure</span><span class="o">[</span><span class="kt">A</span><span class="o">](</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">=</span> <span class="n">a</span> <span class="o">::</span> <span class="nc">Nil</span>
<span class="k">def</span> <span class="nf">flatMap</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">la</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">A</span><span class="o">])(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="nc">List</span><span class="o">[</span><span class="kt">B</span><span class="o">])</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span> <span class="k">=</span>
  <span class="n">la</span> <span class="k">match</span> <span class="o">{</span>
    <span class="k">case</span> <span class="nc">Nil</span> <span class="k">=&gt;</span> <span class="nc">Nil</span>
    <span class="k">case</span> <span class="n">head</span> <span class="o">::</span> <span class="n">tail</span> <span class="k">=&gt;</span> <span class="nf">f</span><span class="o">(</span><span class="n">head</span><span class="o">)</span> <span class="o">:::</span> <span class="nf">flatMap</span><span class="o">(</span><span class="n">tail</span><span class="o">)(</span><span class="n">f</span><span class="o">)</span>
  <span class="o">}</span></code></pre></figure>

<p>So is <code class="language-plaintext highlighter-rouge">Option</code>:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">def</span> <span class="nf">pure</span><span class="o">[</span><span class="kt">A</span><span class="o">](</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">Option</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">=</span> <span class="nc">Some</span><span class="o">(</span><span class="n">a</span><span class="o">)</span>
<span class="k">def</span> <span class="nf">flatMap</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">oa</span><span class="k">:</span> <span class="kt">Option</span><span class="o">[</span><span class="kt">A</span><span class="o">])(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="nc">Option</span><span class="o">[</span><span class="kt">B</span><span class="o">])</span><span class="k">:</span> <span class="kt">Option</span><span class="o">[</span><span class="kt">B</span><span class="o">]</span> <span class="k">=</span>
  <span class="n">oa</span> <span class="k">match</span> <span class="o">{</span>
    <span class="k">case</span> <span class="nc">None</span> <span class="k">=&gt;</span> <span class="nc">None</span>
    <span class="k">case</span> <span class="nc">Some</span><span class="o">(</span><span class="n">value</span><span class="o">)</span> <span class="k">=&gt;</span> <span class="nf">f</span><span class="o">(</span><span class="n">value</span><span class="o">)</span>
  <span class="o">}</span></code></pre></figure>

<h4 id="the-state-monad">The State Monad</h4>

<p>The State Monad describes a transformation over some state, <code class="language-plaintext highlighter-rouge">S</code>, with a result
of <code class="language-plaintext highlighter-rouge">A</code>:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">type</span> <span class="kt">State</span><span class="o">[</span><span class="kt">S</span>, <span class="kt">+A</span><span class="o">]</span> <span class="k">=</span> <span class="n">S</span> <span class="k">=&gt;</span> <span class="o">(</span><span class="n">S</span><span class="o">,</span> <span class="n">A</span><span class="o">)</span></code></pre></figure>

<p>Note that State is a Monad on <code class="language-plaintext highlighter-rouge">A</code>, not on <code class="language-plaintext highlighter-rouge">S</code>!</p>

<p>To prove that State is a Monad, we can write definitions for <code class="language-plaintext highlighter-rouge">pure</code> and
<code class="language-plaintext highlighter-rouge">flatMap</code>:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">implicit</span> <span class="k">def</span> <span class="nf">stateMonad</span><span class="o">[</span><span class="kt">S</span><span class="o">]</span><span class="k">:</span> <span class="kt">Monad</span><span class="o">[</span><span class="kt">State</span><span class="o">[</span><span class="kt">S</span>, <span class="kt">*</span><span class="o">]]</span> <span class="k">=</span> <span class="k">new</span> <span class="nc">Monad</span><span class="o">[</span><span class="kt">State</span><span class="o">[</span><span class="kt">S</span>, <span class="kt">*</span><span class="o">]]</span> <span class="o">{</span>
  <span class="k">def</span> <span class="nf">pure</span><span class="o">[</span><span class="kt">A</span><span class="o">](</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">State</span><span class="o">[</span><span class="kt">S</span>, <span class="kt">A</span><span class="o">]</span> <span class="k">=</span>
    <span class="n">s</span> <span class="k">=&gt;</span> <span class="o">(</span><span class="n">s</span><span class="o">,</span> <span class="n">a</span><span class="o">)</span>
  <span class="k">def</span> <span class="nf">flatMap</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">B</span><span class="o">](</span><span class="n">sa</span><span class="k">:</span> <span class="kt">State</span><span class="o">[</span><span class="kt">S</span>, <span class="kt">A</span><span class="o">])(</span><span class="n">f</span><span class="k">:</span> <span class="kt">A</span> <span class="o">=&gt;</span> <span class="nc">State</span><span class="o">[</span><span class="kt">S</span>, <span class="kt">B</span><span class="o">])</span> <span class="k">=</span>
    <span class="n">originalS</span> <span class="k">=&gt;</span> <span class="o">{</span>
      <span class="nf">val</span> <span class="o">(</span><span class="n">newS</span><span class="o">,</span> <span class="n">value</span><span class="o">)</span> <span class="k">=</span> <span class="nf">sa</span><span class="o">(</span><span class="n">originalS</span><span class="o">)</span>
      <span class="k">val</span> <span class="nv">newState</span> <span class="k">=</span> <span class="nf">f</span><span class="o">(</span><span class="n">value</span><span class="o">)</span>
      <span class="nf">newState</span><span class="o">(</span><span class="n">newS</span><span class="o">)</span>
    <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<h4 id="purity-and-side-effects">Purity and Side Effects</h4>

<p>A concept that comes up often in functional programming is that of purity,
because it not only helps compilers optimize programs but helps humans
understand what certain functions can do.</p>

<p>One part of purity is referential transparency. For example, with</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">def</span> <span class="nf">myValue</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">3</span></code></pre></figure>

<p>it could be said that <code class="language-plaintext highlighter-rouge">myValue</code> is referentially transparent, because it
returns the same value for the same input (similar to the concept of a
mathematical function). However, it could not be said with</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">def</span> <span class="nf">myValueImpure</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="o">{</span>
  <span class="nv">Random</span><span class="o">.</span><span class="py">nextInt</span><span class="o">(</span><span class="mi">6</span><span class="o">)</span>
<span class="o">}</span></code></pre></figure>

<p>that <code class="language-plaintext highlighter-rouge">myValueImpure</code> is referentially transparent, because it will return
different values for the same input.</p>

<p>The second part of purity is whether a function causes a side effect. A side
effect is any call/function/procedure that changes or requires external state.
Examples of side effects are:</p>
<ul>
  <li>Printing a line</li>
  <li>Reading a file</li>
  <li>Making a web request</li>
  <li>Calling another executable</li>
</ul>

<h4 id="the-state-monad-as-computation">The State Monad as Computation</h4>

<p>Let’s create a Monad, <code class="language-plaintext highlighter-rouge">IO</code>, that is a <code class="language-plaintext highlighter-rouge">State</code> on the <code class="language-plaintext highlighter-rouge">RealWorld</code>:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">type</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">=</span> <span class="nc">State</span><span class="o">[</span><span class="kt">RealWorld</span>, <span class="kt">A</span><span class="o">]</span></code></pre></figure>

<p>Here, <code class="language-plaintext highlighter-rouge">RealWorld</code> is entirely hypothetical, and we will have a hypothetical
“macro” called <code class="language-plaintext highlighter-rouge">impure</code> that turns a codeblock which returns <code class="language-plaintext highlighter-rouge">A</code> into one which
returns <code class="language-plaintext highlighter-rouge">IO[A]</code>. For example:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">def</span> <span class="nf">puts</span><span class="o">(</span><span class="n">str</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">Unit</span><span class="o">]</span> <span class="k">=</span> <span class="nf">impure</span><span class="o">(</span><span class="nf">println</span><span class="o">(</span><span class="n">str</span><span class="o">))</span>
<span class="k">val</span> <span class="nv">gets</span><span class="k">:</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">String</span><span class="o">]</span> <span class="k">=</span> <span class="nf">impure</span><span class="o">(</span><span class="nv">Stdio</span><span class="o">.</span><span class="py">readLine</span><span class="o">())</span></code></pre></figure>

<p>We also have a hypothetical <code class="language-plaintext highlighter-rouge">IOApp</code> that can pass an initial <code class="language-plaintext highlighter-rouge">RealWorld</code> to any
<code class="language-plaintext highlighter-rouge">IO</code>:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">object</span> <span class="nc">Echo</span> <span class="k">extends</span> <span class="nc">IOApp</span> <span class="o">{</span>
  <span class="k">override</span> <span class="k">def</span> <span class="nf">run</span><span class="k">:</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">Unit</span><span class="o">]</span> <span class="k">=</span> <span class="nv">gets</span><span class="o">.</span><span class="py">flatMap</span><span class="o">(</span><span class="n">puts</span><span class="o">)</span>
<span class="o">}</span></code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">puts</code>, <code class="language-plaintext highlighter-rouge">gets</code>, and <code class="language-plaintext highlighter-rouge">run</code> are all pure, because for the same <code class="language-plaintext highlighter-rouge">RealWorld</code>,
they’ll behave the same way and output the same value!</p>

<h2 id="an-overview-of-final-tagless">An Overview of Final Tagless</h2>

<p>Final Tagless is composed of three parts:</p>
<ul>
  <li>The <code class="language-plaintext highlighter-rouge">Language[Wrapper[_]]</code></li>
  <li>The <code class="language-plaintext highlighter-rouge">Interpreter &lt;: Language[α]</code></li>
  <li>The <code class="language-plaintext highlighter-rouge">Program[Result]</code></li>
</ul>

<p>Let’s take an example where we can increment numbers:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">trait</span> <span class="nc">Language</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">[</span><span class="k">_</span><span class="o">]]</span> <span class="o">{</span>
  <span class="k">def</span> <span class="nf">lift</span><span class="o">(</span><span class="n">number</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span>
  <span class="k">def</span> <span class="nf">increment</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span>
<span class="o">}</span></code></pre></figure>

<p>This language enables two things: lifting a Scala <code class="language-plaintext highlighter-rouge">Int</code> into a wrapped <code class="language-plaintext highlighter-rouge">Int</code>,
and incrementing a wrapped <code class="language-plaintext highlighter-rouge">Int</code>. We can implement this language purely like
so:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">type</span> <span class="kt">Id</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">=</span> <span class="n">A</span> <span class="c1">// Id is short for Identity</span>
<span class="k">object</span> <span class="nc">PureInterpreter</span> <span class="k">extends</span> <span class="nc">Language</span><span class="o">[</span><span class="kt">Id</span><span class="o">]</span> <span class="o">{</span>
  <span class="k">def</span> <span class="nf">lift</span><span class="o">(</span><span class="n">number</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">number</span> <span class="c1">// Id[Int] =&gt; Int</span>
  <span class="k">def</span> <span class="nf">increment</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">a</span> <span class="o">+</span> <span class="mi">1</span>
<span class="o">}</span></code></pre></figure>

<p>We can also create an interpreter that does not calculate anything, but instead
records our operations in string form:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">type</span> <span class="kt">AlwaysString</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">=</span> <span class="nc">String</span>
<span class="k">object</span> <span class="nc">StringInterpreter</span> <span class="k">extends</span> <span class="nc">Language</span><span class="o">[</span><span class="kt">AlwaysString</span><span class="o">]</span> <span class="o">{</span>
  <span class="k">def</span> <span class="nf">lift</span><span class="o">(</span><span class="n">number</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="c1">// AlwaysString[Int] =&gt; String</span>
    <span class="nv">number</span><span class="o">.</span><span class="py">toString</span>
  <span class="k">def</span> <span class="nf">increment</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="n">s</span><span class="s">"inc $a"</span>
<span class="o">}</span></code></pre></figure>

<blockquote>
  <p>Side note: Any <code class="language-plaintext highlighter-rouge">Language</code> where it is possible to implement an interpreter
with <code class="language-plaintext highlighter-rouge">Id</code> as its <code class="language-plaintext highlighter-rouge">Wrapper</code> also has an implementation for any Monad as its
<code class="language-plaintext highlighter-rouge">Wrapper</code> via <code class="language-plaintext highlighter-rouge">pure</code> and <code class="language-plaintext highlighter-rouge">map</code>/<code class="language-plaintext highlighter-rouge">flatMap</code>/<code class="language-plaintext highlighter-rouge">mapN</code>.</p>
</blockquote>

<p>Lastly, we can model programs that take arbitrary interpreters:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">trait</span> <span class="nc">Program</span><span class="o">[</span><span class="kt">Result</span><span class="o">]</span> <span class="o">{</span>
  <span class="k">def</span> <span class="nf">apply</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">[</span><span class="k">_</span><span class="o">]](</span><span class="n">interpreter</span><span class="k">:</span> <span class="kt">Language</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Result</span><span class="o">]</span>
<span class="o">}</span></code></pre></figure>

<p>And construct them like so:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">object</span> <span class="nc">IncrementFive</span> <span class="k">extends</span> <span class="nc">Program</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="o">{</span>
  <span class="k">def</span> <span class="nf">apply</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">[</span><span class="k">_</span><span class="o">]](</span><span class="n">interpreter</span><span class="k">:</span> <span class="kt">Language</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span>
    <span class="nv">interpreter</span><span class="o">.</span><span class="py">increment</span><span class="o">(</span><span class="nv">interpreter</span><span class="o">.</span><span class="py">lift</span><span class="o">(</span><span class="mi">5</span><span class="o">))</span>
<span class="o">}</span></code></pre></figure>

<p>We can easily apply any interpreter to a program:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="nc">IncrementFive</span><span class="o">(</span><span class="nc">PureInterpreter</span><span class="o">)</span>   <span class="c1">// =&gt; 6: Int</span>
<span class="nc">IncrementFive</span><span class="o">(</span><span class="nc">StringInterpreter</span><span class="o">)</span> <span class="c1">// =&gt; "inc 5": String</span></code></pre></figure>

<h2 id="the-evolution">The Evolution</h2>

<p>Let’s write a simple calculator DSL. The first step to writing a DSL is to
handle the simplest case:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">object</span> <span class="nc">Calculator</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">add</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
  <span class="k">def</span> <span class="nf">subtract</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">a</span> <span class="o">-</span> <span class="n">b</span>
  <span class="k">def</span> <span class="nf">multiply</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">a</span> <span class="o">*</span> <span class="n">b</span>
  <span class="k">def</span> <span class="nf">divide</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="n">a</span> <span class="o">/</span> <span class="n">b</span>

  <span class="k">def</span> <span class="nf">show</span><span class="o">(</span><span class="n">num</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="nv">num</span><span class="o">.</span><span class="py">toString</span>

<span class="o">}</span></code></pre></figure>

<p>This calculator is great, but it can’t output Lisp. Let’s fix that by building
another calculator:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">object</span> <span class="nc">LispCalculator</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">add</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">String</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="n">s</span><span class="s">"(+ $a $b)"</span>
  <span class="k">def</span> <span class="nf">subtract</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">String</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="n">s</span><span class="s">"(- $a $b)"</span>
  <span class="k">def</span> <span class="nf">multiply</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">String</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="n">s</span><span class="s">"(* $a $b)"</span>
  <span class="k">def</span> <span class="nf">divide</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">String</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="n">s</span><span class="s">"(/ $a $b)"</span>

  <span class="k">def</span> <span class="nf">show</span><span class="o">(</span><span class="n">l</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="n">l</span>

<span class="o">}</span></code></pre></figure>

<p>Well, we have a calculator that outputs Lisp, but its not very interchangable
with our normal calculator. It takes a lot of effort to interchange them, and
we can put non-numbers into <code class="language-plaintext highlighter-rouge">LispCalculator</code>:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">val</span> <span class="nv">addTwoThree</span><span class="k">:</span> <span class="kt">Int</span>    <span class="o">=</span> <span class="nv">Calculator</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">3</span><span class="o">)</span>
<span class="k">val</span> <span class="nv">addTwoThree</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="nv">LispCalculator</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="s">"2"</span><span class="o">,</span> <span class="s">"3"</span><span class="o">)</span>

<span class="k">val</span> <span class="nv">unwanted</span><span class="k">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="nv">LispCalculator</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="s">"apples"</span><span class="o">,</span> <span class="s">"oranges"</span><span class="o">)</span></code></pre></figure>

<h3 id="the-first-step">The First Step</h3>

<p>The first problem is that calculators don’t share behavior. We want to have the
ability to define calculators in a generic way:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="c1">// A is short for Arithmetic</span>
<span class="c1">// S is short for Show</span>
<span class="k">trait</span> <span class="nc">Calculator</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">S</span><span class="o">]</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">add</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">A</span>
  <span class="k">def</span> <span class="nf">subtract</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">A</span>
  <span class="k">def</span> <span class="nf">multiply</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">A</span>
  <span class="k">def</span> <span class="nf">divide</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">,</span> <span class="n">b</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">A</span>

  <span class="k">def</span> <span class="nf">show</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">A</span><span class="o">)</span><span class="k">:</span> <span class="kt">S</span>

<span class="o">}</span></code></pre></figure>

<p>We can copy over our old pure calculator, as well as our Lisp calculator:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">object</span> <span class="nc">PureCalculator</span> <span class="k">extends</span> <span class="nc">Calculator</span><span class="o">[</span><span class="kt">Int</span>, <span class="kt">String</span><span class="o">]</span> <span class="o">{</span> <span class="cm">/* ... */</span> <span class="o">}</span>

<span class="k">object</span> <span class="nc">LispCalculator</span> <span class="k">extends</span> <span class="nc">Calculator</span><span class="o">[</span><span class="kt">String</span>, <span class="kt">String</span><span class="o">]</span> <span class="o">{</span> <span class="cm">/* ... */</span> <span class="o">}</span></code></pre></figure>

<p>However, we run into two new problems with generic computation. It’s impossible
to define a return type for <code class="language-plaintext highlighter-rouge">calculate</code> that lets the <code class="language-plaintext highlighter-rouge">Calculation</code> both
perform arithmetic as well as <code class="language-plaintext highlighter-rouge">show</code>, and its impossible for <code class="language-plaintext highlighter-rouge">calculate</code> to do
anything in the first place as it doesn’t know how to make values of type <code class="language-plaintext highlighter-rouge">A</code>
or <code class="language-plaintext highlighter-rouge">S</code>:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">trait</span> <span class="nc">Calculation</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">calculate</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">S</span><span class="o">](</span><span class="n">calculator</span><span class="k">:</span> <span class="kt">Calculator</span><span class="o">[</span><span class="kt">A</span>, <span class="kt">S</span><span class="o">])</span><span class="k">:</span> <span class="kt">???</span>

<span class="o">}</span></code></pre></figure>

<h3 id="the-second-step">The Second Step</h3>

<p>The second problem is that that there’s no way to define a generic computation.
We can solve this by making our calculations and calculator taking some sort of
wrapper type that handles arithmetic, <code class="language-plaintext highlighter-rouge">show</code>, and any types that may be added
in the future, as well as defining a calculator  method to lift arithmetic
values:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">trait</span> <span class="nc">Calculator</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">[</span><span class="k">_</span><span class="o">]]</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">lift</span><span class="o">(</span><span class="n">value</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span>

  <span class="k">def</span> <span class="nf">add</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">],</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span>
  <span class="k">def</span> <span class="nf">subtract</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">],</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span>
  <span class="k">def</span> <span class="nf">multiply</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">],</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span>
  <span class="k">def</span> <span class="nf">divide</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">],</span> <span class="n">b</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span>

  <span class="k">def</span> <span class="nf">show</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">String</span><span class="o">]</span>

<span class="o">}</span>

<span class="k">trait</span> <span class="nc">Calculation</span><span class="o">[</span><span class="kt">Result</span><span class="o">]</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">apply</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">[</span><span class="k">_</span><span class="o">]](</span><span class="n">calculator</span><span class="k">:</span> <span class="kt">Calculator</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Result</span><span class="o">]</span>

<span class="o">}</span></code></pre></figure>

<p>This is now a simple Final Tagless DSL! By having the basic goals of generic
computation and generic interpretation, we’ve gone from an incredibly basic DSL
to a still basic, but incredibly powerful one.</p>

<p>Implementing calculators and calculations is super easy, and in fact we can
still copy over most of our other code:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">type</span> <span class="kt">Id</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">=</span> <span class="n">A</span>
<span class="k">object</span> <span class="nc">PureCalculator</span> <span class="k">extends</span> <span class="nc">Calculator</span><span class="o">[</span><span class="kt">Id</span><span class="o">]</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">lift</span><span class="o">(</span><span class="n">value</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">Id</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span> <span class="c1">// Id[Int] =&gt; Int</span>
    <span class="n">value</span> 

  <span class="c1">// ...</span>

<span class="o">}</span>

<span class="k">type</span> <span class="kt">AlwaysString</span><span class="o">[</span><span class="kt">A</span><span class="o">]</span> <span class="k">=</span> <span class="nc">String</span>
<span class="k">object</span> <span class="nc">LispCalculator</span> <span class="k">extends</span> <span class="nc">Calculator</span><span class="o">[</span><span class="kt">AlwaysString</span><span class="o">]</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">lift</span><span class="o">(</span><span class="n">value</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">AlwaysString</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span> <span class="c1">// AlwaysString[Int] =&gt; String</span>
    <span class="nv">value</span><span class="o">.</span><span class="py">toString</span>

  <span class="c1">// ...</span>

<span class="o">}</span>

<span class="k">object</span> <span class="nc">TwoPlusThree</span> <span class="k">extends</span> <span class="nc">Calculation</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">apply</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">[</span><span class="k">_</span><span class="o">]](</span><span class="n">calculator</span><span class="k">:</span> <span class="kt">Calculator</span><span class="o">[</span><span class="kt">Wrapper</span><span class="o">])</span><span class="k">:</span> <span class="kt">Wrapper</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span>
    <span class="nv">calculator</span><span class="o">.</span><span class="py">add</span><span class="o">(</span><span class="nv">calculator</span><span class="o">.</span><span class="py">lift</span><span class="o">(</span><span class="mi">2</span><span class="o">),</span> <span class="nv">calculator</span><span class="o">.</span><span class="py">lift</span><span class="o">(</span><span class="mi">3</span><span class="o">))</span>

<span class="o">}</span>

<span class="nc">TwoPlusThree</span><span class="o">(</span><span class="nc">PureCalculator</span><span class="o">)</span> <span class="c1">// =&gt; 5: Int</span>
<span class="nc">TwoPlusThree</span><span class="o">(</span><span class="nc">LispCalculator</span><span class="o">)</span> <span class="c1">// =&gt; "(+ 2 3)": String</span></code></pre></figure>

<h4 id="using-io-in-calculator">Using IO in Calculator</h4>

<p>Say we want to run calculations by doing web requests. That’s a side effect, so
we need to do it with IO:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">object</span> <span class="nc">WebreqCalculator</span> <span class="k">extends</span> <span class="nc">Calculator</span><span class="o">[</span><span class="kt">IO</span><span class="o">]</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">lift</span><span class="o">(</span><span class="n">value</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span> <span class="nv">IO</span><span class="o">.</span><span class="py">pure</span><span class="o">(</span><span class="n">value</span><span class="o">)</span>

  <span class="k">def</span> <span class="nf">add</span><span class="o">(</span><span class="n">a</span><span class="k">:</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">Int</span><span class="o">],</span> <span class="n">b</span><span class="k">:</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">Int</span><span class="o">])</span><span class="k">:</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">Int</span><span class="o">]</span> <span class="k">=</span>
    <span class="nv">a</span><span class="o">.</span><span class="py">flatMap</span><span class="o">(</span><span class="n">aVal</span> <span class="k">=&gt;</span> <span class="nv">b</span><span class="o">.</span><span class="py">flatMap</span><span class="o">(</span><span class="n">bVal</span> <span class="k">=&gt;</span> <span class="nf">fetch</span><span class="o">(</span><span class="n">s</span><span class="s">"example.com/add/$aVal/$bVal"</span><span class="o">))</span>
  <span class="c1">// etc</span>

<span class="o">}</span></code></pre></figure>

<p>The beauty of using IO for this is that it keeps its properties, even when
passed through a Tagless DSL:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="nc">TwoPlusThree</span><span class="o">(</span><span class="nc">WebreqCalculator</span><span class="o">)</span> <span class="c1">// =&gt; IO(...): IO[Int]</span>
<span class="c1">// No web requests made yet!</span>

<span class="k">object</span> <span class="nc">RunTwoPlusThree</span> <span class="k">extends</span> <span class="nc">IOApp</span> <span class="o">{</span>
  <span class="k">override</span> <span class="k">def</span> <span class="nf">run</span><span class="k">:</span> <span class="kt">IO</span><span class="o">[</span><span class="kt">Unit</span><span class="o">]</span> <span class="k">=</span>
    <span class="nc">TwoPlusThree</span><span class="o">(</span><span class="nc">WebreqCalculator</span><span class="o">).</span><span class="py">flatMap</span><span class="o">(</span><span class="n">puts</span><span class="o">)</span>
    <span class="c1">// Does web requests, prints the result</span>
<span class="o">}</span></code></pre></figure>

<h2 id="summary">Summary</h2>

<p>Always start with the most basic version of your DSL. Then try to generify it
with a hodge-podge of type variables, then see if you can move that to a
wrapper style.</p>

<p>As an exercise, I recommend creating a DSL that handles a simple party game,
with interpreters for both evaluating the state of the party game as well as
outputting a log of the state changes. For example, <a href="https://en.wikipedia.org/wiki/Uno_(card_game)">Uno</a> might look
something along the lines of:</p>

<figure class="highlight"><pre><code class="language-scala" data-lang="scala"><span class="k">case</span> <span class="k">class</span> <span class="nc">UnoState</span><span class="o">(</span>
  <span class="n">current</span><span class="k">:</span> <span class="kt">Card</span><span class="o">,</span>
  <span class="n">numPlayers</span><span class="k">:</span> <span class="kt">Int</span><span class="o">,</span>
  <span class="n">players</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">UnoPlayer</span><span class="o">],</span>
  <span class="n">drawDeck</span><span class="k">:</span> <span class="kt">List</span><span class="o">[</span><span class="kt">Card</span><span class="o">]</span>
<span class="o">)</span>

<span class="k">trait</span> <span class="nc">Uno</span><span class="o">[</span><span class="kt">W</span><span class="o">[</span><span class="k">_</span><span class="o">]]</span> <span class="o">{</span>

  <span class="k">def</span> <span class="nf">newGame</span><span class="o">(</span><span class="n">numPlayers</span><span class="k">:</span> <span class="kt">Int</span><span class="o">)</span><span class="k">:</span> <span class="kt">W</span><span class="o">[</span><span class="kt">UnoState</span><span class="o">]</span>

<span class="o">}</span></code></pre></figure>

<blockquote>
  <p>Side note: another good exercise is to take the Calculator code here and
make a <code class="language-plaintext highlighter-rouge">MonadCalculator</code> that works for any <code class="language-plaintext highlighter-rouge">M: Monad</code>.</p>
</blockquote>]]></content><author><name></name></author><category term="programming" /><category term="scala" /><summary type="html"><![CDATA[Introduction]]></summary></entry></feed>