new website

This commit is contained in:
Pat Garrity 2025-09-26 21:42:27 -05:00
parent 17e6bd38e3
commit 464f925c5c
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
10 changed files with 670 additions and 460 deletions

View file

@ -1,160 +0,0 @@
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
font-size: 100%;
line-height: 1;
font-family: Roboto, sans-serif;
margin: 0;
padding: 0;
color: #000000;
}
a {
color: #222222;
font-weight: bold;
}
a:visited {
color: #222222;
}
a:hover {
color: #333333;
text-decoration: none;
}
div#container {
width: 90ch;
margin-left: auto;
margin-right: auto;
}
header {
text-align: center;
}
header h1 {
font-size: 2.5rem;
font-family: Iosevka, monospace;
}
nav {
font-family: Iosevka, monospace;
font-size: 1.2rem;
background-color: #e3e3e3;
padding: 0.5rem;
text-align: center;
}
nav ul {
list-style: none;
margin: 0;
padding: 0;
}
nav ul li {
display: inline-block;
margin: 0;
padding: 0;
}
nav ul li:not(:first-child) {
margin-left: 1rem;
}
nav a {
font-weight: 800;
}
main {
line-height: 1.25;
font-size: 1.1rem;
}
main a:hover {
color: #000;
}
main h1 {
padding: 0;
border-bottom: 1px dashed #333;
font-family: Mina, Iosevka, monospace;
font-size: 2rem;
}
main h2 {
padding: 0;
font-family: Mina, Iosevka, monospace;
font-size: 1.5rem;
}
dl {
padding: 1rem;
background-color: #f6f6f6;
}
dl dt {
font-weight: 800;
}
dl dt:not(:first-child) {
margin-top: 0.75rem;
}
dl dd {
margin: 0;
padding: 0;
}
footer {
margin-top: 1rem;
border-top: 1px solid #cccccc;
padding-top: 1rem;
margin-bottom: 1rem;
}
table, tr, td {
margin: 0;
padding: 0;
border: 0;
}
dl.project-list {
width: 100%;
font-size: 1rem;
padding: 0;
margin: 0;
background-color: #fff;
}
dl.project-list dt {
width: 100%;
margin-top: 1rem;
padding: 0.5rem;
display: block;
background-color: #f0f0f0;
border-top: 1px solid #999;
border-left: 1px solid #999;
border-right: 1px solid #999;
font-family: Iosevka, monospace;
}
dl.project-list dd {
width: 100%;
padding: 0.5rem;
border-bottom: 1px solid #999;
border-left: 1px solid #999;
border-right: 1px solid #999;
}
pre {
font-family: Iosevka, monospace;
font-size: 1rem;
color: #000;
padding: 0.5rem;
border: 1px solid #ccc;
}

View file

@ -1,55 +1,53 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<title>Garrity Software - Contact</title>
<meta name="author" content="pfm" />
<meta name="description" content="Garrity Software - Contact" />
<link rel="stylesheet" type="text/css" href="assets/gs.css">
</head>
<body>
<head>
<title>Garrity Software - Contact</title>
<meta name="author" content="pfm" />
<meta name="description" content="garrity software contact information" />
<link rel="stylesheet" type="text/css" href="gs.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
</head>
<body>
<div id="wrapper">
<header><a href="https://garrity.co">λ garrity software</a></header>
<nav>
<ul>
<li><a href="index.html">about</a></li>
<li><a href="open-source.html">open source</a></li>
<li><a href="writing.html">writing</a></li>
<li><a href="contact.html">contact</a></li>
</ul>
</nav>
<main>
<section>
<h1>Email</h1>
<p><code>pfm@garrity.co</code></p>
</section>
<div id="container">
<section>
<h1>Social Media</h1>
<p>I do not use any social media platforms.</p>
</section>
<!-- Header/Logo -------------------------------------------------------------->
<header>
<h1>garrity software</h1>
</header>
<section>
<h1>Chat</h1>
<p>Email me for an invitation to the garrity-software
<a class="external" href="https://zulip.com/">Zulip</a> organization.
Zulip provides web and desktop clients.</p>
</section>
<!-- Navigation --------------------------------------------------------------->
<nav>
<ul>
<li><a href="index.html">about</a></li>
<li><a href="oss.html">open source</a></li>
<li><a href="donate.html">donate</a></li>
<li><a href="contact.html">contact</a></li>
</ul>
</nav>
<!-- Main Content ------------------------------------------------------------->
<main>
<section id="contact">
<h1>Contact</h1>
<h2>Community Discord</h2>
<p><a href="https://discord.gg/nphkquBhw3">The Garrity Software Discord Community</a>
is intended to be a place to discuss and ask questions about open source
software, as well as discuss GS or software development in general. This Discord
server should not be used for product support.</p>
<p>This is also a viable option for getting in touch with Pat directly.</p>
<h2>Email</h2>
<p>pfm@garrity.co is an appropriate email address for reaching out to Pat
directly.</p>
</section>
</main>
<!-- Footer ------------------------------------------------------------------->
<footer>
Content Copyright © Patrick Garrity
</footer>
</div>
</body>
<section>
<h1>Git</h1>
<p>All source repositories are currently hosted under
<a class="external" href="https://git.garrity.co">git.garrity.co</a>
(my self-hosted <a class="external" href="https://forgejo.org/">Forgejo</a> instance),
though I may mirror open source repositories on
<a class="external" href="https://codeberg.org/">codeberg</a>.</p>
</section>
</main>
<footer>Copyright <span class="focus">pfm</span></footer>
</div>
</body>
</html>

View file

@ -1,64 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Garrity Software - Donate</title>
<meta name="author" content="pfm" />
<meta name="description" content="Garrity Software - Donate" />
<link rel="stylesheet" type="text/css" href="assets/gs.css">
</head>
<body>
<div id="container">
<!-- Header/Logo -------------------------------------------------------------->
<header>
<h1>garrity software</h1>
</header>
<!-- Navigation --------------------------------------------------------------->
<nav>
<ul>
<li><a href="index.html">about</a></li>
<li><a href="oss.html">open source</a></li>
<li><a href="donate.html">donate</a></li>
<li><a href="contact.html">contact</a></li>
</ul>
</nav>
<!-- Main Content ------------------------------------------------------------->
<main>
<section id="donate">
<h1>Donate</h1>
<p>Garrity Software is a fledgling organization that produces open source
libraries and intends to eventually sell software. If you like the open source
work, or you want to support Pat in one day going independent:</p>
<p><strong><a href="https://ko-fi.com/gspfm">Donate to Pat on Ko-fi</a></strong></p>
<h2>What do my donations do?</h2>
<p>Donations help to cover the costs of anything related to open source
development (servers, subscriptions, etc.) or community development. Donations
also serve as a general source of encouragement, and help move the needle
towards independence.</p>
<p>Donations are not (and will not be) used for any paid product hosting or
other payment-related services.</p>
</section>
<section id="sponsor">
<h1>Sponsorship</h1>
<p>If, for some reason, your company wants to <em>sponsor</em> Garrity Software,
please <a href="contact.html">contact Pat directly</a>.</p>
</section>
</main>
<!-- Footer ------------------------------------------------------------------->
<footer>
Content Copyright © Patrick Garrity
</footer>
</div>
</body>
</html>

220
src/gs.css Normal file
View file

@ -0,0 +1,220 @@
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
html {
font-size: 16px;
}
body {
line-height: 1.2;
font-family: "Libre Baskerville", serif;
margin: 0;
padding: 0;
color: #000000;
}
::selection {
color: #f0f0f0;
background-color: #000000;
}
div#wrapper {
margin-top: 1rem;
margin-left: auto;
margin-right: auto;
width: 90ch;
}
header {
font-size: 1.15rem;
width: 100%;
text-align: center;
}
header a {
color: #000000;
display: inline-block;
text-decoration: none;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
header a:visited {
color: #000000;
}
header a:hover {
text-decoration: underline;
}
nav {
font-size: 1.25rem;
margin-top: 1rem;
margin-bottom: 1rem;
width: 100%;
background-color: #f6f6f6;
border: 1px solid #ccc;
text-align: center;
}
nav ul {
list-style: none;
margin: 0;
padding: 0;
}
nav ul li {
display: inline-block;
margin: 0;
padding: 0;
}
nav ul li:not(:first-child) {
margin-left: 0rem;
}
nav a {
color: #333333;
padding: 0.5rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
display: inline-block;
text-decoration: none;
}
nav a:visited {
color: #333333;
}
nav a:hover {
background-color: #ffffff;
}
main {
font-size: 1.1rem;
}
footer {
font-size: 1rem;
margin-top: 3rem;
border-top: 1px solid #f0f0f0;
padding-top: 0.5rem;
text-align: center;
color: #666;
}
h1 {
font-size: 1.6rem;
font-family: Lato;
padding-bottom: 0.2rem;
border-bottom: 1px solid #e0e0e0;
display: block;
}
h2 {
font-size: 1.4rem;
font-family: Lato;
padding-bottom: 0.2rem;
border-bottom: 1px solid #e0e0e0;
display: block;
}
h3 {
font-size: 1.2rem;
font-family: Lato;
display: block;
}
span.focus {
font-variant: small-caps;
}
main a {
color: #444444;
}
main a:visited {
color: #000000;
}
main a:hover {
color: #777777;
}
main a.internal {
text-decoration: none;
border-bottom: 1px solid #333333;
padding-bottom: 0.1rem;
padding-top: 0;
}
main a.internal:hover {
color: #777777;
border-bottom: 1px solid #cccccc;
}
main a.external {
text-decoration: none;
border-bottom: 1px dotted #333333;
padding: 0.1rem;
padding-top: 0;
}
main a.external:hover {
color: #777777;
border-bottom: 1px dotted #cccccc;
}
ul#entry-list {
width: 100%;
list-style: none;
margin: 0;
padding: 0;
border: 1px solid #999999;
border-bottom: 0;
font-size: 1rem;
}
ul#entry-list li {
color: #222222;
display: block;
margin: 0;
padding: 1rem;
width: 100%;
border: 0;
border-bottom: 1px solid #999999;
transition: background 0.5s ease;
}
ul#entry-list li:hover {
border-bottom: 1px solid #999999;
background-color: #f0f0f0;
}
ul#entry-list a {
display: block;
padding: 0.5rem;
text-decoration: none;
}
ul#entry-list a:hover {
text-decoration: underline;
}
ul#entry-list li div.tags {
margin-left: 1rem;
display: inline-block;
}
small {
border-radius: 6px;
font-size: 0.8rem;
padding: 0.25rem;
color: #333333;
background-color: #cccccc;
border: 1px solid #cccccc;
display: inline-block;
}

View file

@ -1,53 +1,75 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<title>Garrity Software - About</title>
<meta name="author" content="pfm" />
<meta name="description" content="Garrity Software Homepage" />
<link rel="stylesheet" type="text/css" href="assets/gs.css">
</head>
<body>
<head>
<title>Garrity Software</title>
<meta name="author" content="pfm" />
<meta name="description" content="garrity software homepage" />
<link rel="stylesheet" type="text/css" href="gs.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
</head>
<body>
<div id="wrapper">
<header><a href="https://garrity.co">λ garrity software</a></header>
<nav>
<ul>
<li><a href="index.html">about</a></li>
<li><a href="open-source.html">open source</a></li>
<li><a href="writing.html">writing</a></li>
<li><a href="contact.html">contact</a></li>
</ul>
</nav>
<main>
<section>
<p><em>garrity software</em> (<span class="focus">gs</span>) is my software organization.
It is currently focused on open source projects and writing. It is
intended to one day develop software products.</p>
</section>
<div id="container">
<section>
<h1>Who Am I?</h1>
<p><span class="focus">pfm</span> [Pat] &mdash; Presumably human;
programming enthusiast and professional.</p>
</section>
<!-- Header/Logo -------------------------------------------------------------->
<header>
<h1>garrity software</h1>
</header>
<section>
<h1>What I Do</h1>
<p><span class="focus">gs</span> produces libraries and
applications that support things I enjoy working on, such as
note taking, developer tooling, and software development;
I create libraries intended to be used in general settings.</p>
<!-- Navigation --------------------------------------------------------------->
<nav>
<ul>
<li><a href="index.html">about</a></li>
<li><a href="oss.html">open source</a></li>
<li><a href="donate.html">donate</a></li>
<li><a href="contact.html">contact</a></li>
</ul>
</nav>
<p>For a list of <span class="focus">gs</span> projects,
please refer to <a class="internal" href="open-source.html">open source</a>.</p>
<!-- Main Content ------------------------------------------------------------->
<main>
<section id="short-description">
<p><em>Garrity Software</em> (GS) is an organization devoted to producing high
quality, robust software. GS is also committed to producing as much free and
open source content as possible.</p>
<p>At this point in time, GS is not in a position to release commerical
software. Product development may occur in the future.</p>
</section>
<p>I also <a class="internal" href="writing.html">write</a> about software from time to time.</p>
<section id="team">
<h1>Team</h1>
<h2>Pat Garrity (pfm)</h2>
<p>Founder and visionary of Garrity Software.</p>
</section>
</main>
<h2>Languages &amp; Ecosystems</h2>
<p>I primarily work in
<a class="external" href="https://scala-lang.org/">Scala 3</a>,
leveraging the <a class="external" href="https://typelevel.org/cats/">Cats</a> ecosystem.</p>
<!-- Footer ------------------------------------------------------------------->
<footer>
Content Copyright © Patrick Garrity
</footer>
<h2>My Development Approach</h2>
<p>I strive to write <em>robust</em> code that meets its goals and stands the test of time.
I enjoy rapid iteration backed by thought and preparation. I work best when I'm having fun,
and work best when I'm working on an interesting problem.</p>
</section>
</div>
<section>
<h1>My Goals</h1>
<p>One day I hope to commit entirely to <span class="focus">gs</span>, producing software
and documentation that I find useful, releasing as much as possible for free. My minimum
runway to this end appears to be 2030 - I expect it to be further in the future, and the
ultimate timeline depends on my family and what I can scrape together at night. In the
meantime, my goal is to keep coding in my free time and preserve my love for my craft.</p>
</body>
<p>If you want to support my goals, discuss software with me in my <a href="contact.html">Zulip organization</a> or
<a class="external" href="https://ko-fi.com/gspfm">donate</a> if you find my contributions useful and are
in a position to do so.</p>
</section>
</main>
<footer>Copyright <span class="focus">pfm</span></footer>
</div>
</body>
</html>

64
src/open-source.html Normal file
View file

@ -0,0 +1,64 @@
<!doctype html>
<html>
<head>
<title>Garrity Software - Open Source</title>
<meta name="author" content="pfm" />
<meta name="description" content="garrity software open source" />
<link rel="stylesheet" type="text/css" href="gs.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
</head>
<body>
<div id="wrapper">
<header><a href="https://garrity.co">λ garrity software</a></header>
<nav>
<ul>
<li><a href="index.html">about</a></li>
<li><a href="open-source.html">open source</a></li>
<li><a href="writing.html">writing</a></li>
<li><a href="contact.html">contact</a></li>
</ul>
</nav>
<main>
<section>
<ul id="entry-list">
<li>
<a href="https://git.garrity.co/garrity-software/gs-uuid">gs-uuid</a>
<div class="tags">
<small>2025-07-23</small>
<small>scala</small>
<small>library</small>
</div>
</li>
<li>
<a href="https://git.garrity.co/garrity-software/gs-timing">gs-timing</a>
<div class="tags">
<small>2025-07-23</small>
<small>scala</small>
<small>library</small>
</div>
</li>
<li>
<a href="https://git.garrity.co/garrity-software/gs-datagen">gs-datagen</a>
<div class="tags">
<small>2025-07-23</small>
<small>scala</small>
<small>library</small>
</div>
</li>
<li>
<a href="https://git.garrity.co/garrity-software/gs-config">gs-config</a>
<div class="tags">
<small>2025-07-23</small>
<small>scala</small>
<small>library</small>
</div>
</li>
</ul>
</section>
</main>
<footer>Copyright <span class="focus">pfm</span></footer>
</div>
</body>
</html>

View file

@ -1,143 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Garrity Software - Open Source</title>
<meta name="author" content="pfm" />
<meta name="description" content="Garrity Software - Open Source" />
<link rel="stylesheet" type="text/css" href="assets/gs.css">
</head>
<body>
<div id="container">
<!-- Header/Logo -------------------------------------------------------------->
<header>
<h1>garrity software</h1>
</header>
<!-- Navigation --------------------------------------------------------------->
<nav>
<ul>
<li><a href="index.html">about</a></li>
<li><a href="oss.html">open source</a></li>
<li><a href="donate.html">donate</a></li>
<li><a href="contact.html">contact</a></li>
</ul>
</nav>
<!-- Main Content ------------------------------------------------------------->
<main>
<section id="open-source">
<h1>Open Source</h1>
<p>Garrity Software exposes all non-product software as open source. The MIT
License is standard for GS projects.</p>
<p>If you have questions or want to discuss these projects, consider joining the
<a href="https://discord.gg/nphkquBhw3">Garrity Software Discord Community</a>.</p>
</section>
<section id="vcs">
<h2>Version Control (Git)</h2>
<p>GS uses self-hosted version control at
<a href="https://git.garrity.co/garrity-software">https://git.garrity.co/garrity-software</a>.
All code is available, though registration and contributions are not accepted at
this time.</p>
<h3>Mirrors</h3>
<p>GS does not currently mirror repositories.</p>
</section>
<section id="maven">
<h2>Maven</h2>
<p>GS provides a Maven server and does not upload artifacts to other public
Maven/Ivy repositories at this time.</p>
<pre><code>resolvers += "garrity-software-gs" at "https://maven.garrity.co/gs"</code></pre>
<p>No credentials are required to use this repository.</p>
</section>
<section id="api-docs">
<h2>API Documentation</h2>
<p>Code documentation is not yet published publicly.</p>
</section>
<section id="projects">
<h2>Projects</h2>
<dl class="project-list">
<dt><a href="https://git.garrity.co/garrity-software/gs-uuid">gs-uuid</a>
(Scala 3) (Library)</dt>
<dd>Uses <a href="https://github.com/cowtowncoder/java-uuid-generator/">JUG</a>
and ported code from Jackson to provide an opaque <code>UUID</code> type
over <code>java.util.UUID</code>, backed by custom rendering and parsing.</dd>
<dt><a href="https://git.garrity.co/garrity-software/gs-slug">gs-slug</a>
(Scala 3) (Library)</dt>
<dd>Provides a <code>Slug</code> type. This type relies on a very small set of
ASCII characters and is intended for URL-safe identifiers.</dd>
<dt><a href="https://git.garrity.co/garrity-software/gs-hex">gs-hex</a>
(Scala 3) (Library)</dt>
<dd>Small, efficient, Hexadecimal conversion library with encoder/decoder type
classes and support for basic types. Based on handling byte arrays.</dd>
<dt><a href="https://git.garrity.co/garrity-software/gs-blob">gs-blob</a>
(Scala 3) (Library)</dt>
<dd>Opaque type (<code>Blob</code>) and tools (e.g. encoding) for "blobs" -
arrays of bytes.</dd>
<dt><a href="https://git.garrity.co/garrity-software/gs-datagen">gs-datagen</a>
(Scala 3) (Library)</dt>
<dd>Random data generation library for Scala 3. Intended for use in tests, but
not limited to that case. Provides a composable type for generators and
several standard generators.</dd>
<dt><a href="https://git.garrity.co/garrity-software/gs-config">gs-config</a>
(Scala 3) (Library)</dt>
<dd>Library for loading, and specifically <em>auditing</em>, configuration.
Provides a complete digest of what configuration keys were queried and what
happened to them.</dd>
</dl>
</section>
<section id="incubator">
<h2>Incubator</h2>
<dl class="project-list">
<dt><a href="https://git.garrity.co/garrity-software/gs-test">gs-test</a>
(Scala 3) (Library)</dt>
<dd>Test framework for Scala 3. Based on Cats Effect. Notably provides
structured test output and envisions a more powerful method of expressing
test execution.</dd>
<dt><a href="https://git.garrity.co/garrity-software/gs-log">gs-log</a>
(Scala 3) (Library)</dt>
<dd>FP logging library for Scala 3.</dd>
<dt><a href="https://git.garrity.co/garrity-software/gs-crypto">gs-crypto</a>
(Scala 3) (Library)</dt>
<dd>Based on JVM standard implementations and Bouncy Castle. Working to define
some crypto interfaces for GS projects in a way that works well with
Scala.</dd>
<dt><a href="https://git.garrity.co/garrity-software/smolban">smolban</a>
(Scala 3) (Application)</dt>
<dd>Fun side-project to create a small/minimal Kanban oriented system. Light on
process, helps prove out certain concepts in a non-critical
application.</dd>
</dl>
</section>
</main>
<!-- Footer ------------------------------------------------------------------->
<footer>
Content Copyright © Patrick Garrity
</footer>
</div>
</body>
</html>

40
src/writing.html Normal file
View file

@ -0,0 +1,40 @@
<!doctype html>
<html>
<head>
<title>Garrity Software - Writing</title>
<meta name="author" content="pfm" />
<meta name="description" content="garrity software writing" />
<link rel="stylesheet" type="text/css" href="gs.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
</head>
<body>
<div id="wrapper">
<header><a href="https://garrity.co">λ garrity software</a></header>
<nav>
<ul>
<li><a href="index.html">about</a></li>
<li><a href="open-source.html">open source</a></li>
<li><a href="writing.html">writing</a></li>
<li><a href="contact.html">contact</a></li>
</ul>
</nav>
<main>
<section>
<ul id="entry-list">
<li>
<a href="writing/semantic-type-refinement.html">Semantic Type Refinement</a>
<div class="tags">
<small>2025-09-25</small>
<small>scala</small>
<small>types</small>
</div>
</li>
</ul>
</section>
</main>
<footer>Copyright <span class="focus">pfm</span></footer>
</div>
</body>
</html>

View file

@ -0,0 +1,73 @@
<!doctype html>
<html>
<head>
<title>Garrity Software - Optimizing Shell History</title>
<meta name="author" content="pfm" />
<meta name="description" content="writing - optimizing shell history" />
<link rel="stylesheet" type="text/css" href="../gs.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/go.min.js"></script>
</head>
<body>
<div id="wrapper">
<header><a href="#">λ garrity software</a></header>
<nav>
<ul>
<li><a href="../index.html">about</a></li>
<li><a href="../open-source.html">open source</a></li>
<li><a href="../writing.html">writing</a></li>
<li><a href="../contact.html">contact</a></li>
</ul>
</nav>
<main>
<section>
<h1>Optimizing Shell History</h1>
<p>I've spent a lot of time fiddling with my local development environment over
the years. As a result, I've also evolved how I interact with my shell history.</p>
<p>I would say that I use my history as a convenience and efficiency booster,
though I do not use it to drive workflow to the extent I see from others.</p>
<p>I take advantage of three things:</p>
<ol>
<li>History configuration settings.</li>
<li>Shell comments.</li>
<li>Fuzzy search (FZF, in this case).</li>
</ol>
</section>
<section>
<h2 id="history-configuration">History Configuration</h2>
<p>I currently use <code>zsh</code>, so the example below is
for <code>zsh</code>.</p>
<pre><code class="language-bash">export HISTFILE="$HOME/.zsh_history"
export HISTSIZE=25000
export SAVEHIST=$HISTSIZE
export HISTORY_IGNORE="(ls|cd|pwd|exit)*"
setopt EXTENDED_HISTORY # Write the history file in the ':start:elapsed;command' format.
setopt INC_APPEND_HISTORY # Write to the history file immediately, not when the shell exits.
setopt SHARE_HISTORY # Share history between all sessions.
setopt HIST_IGNORE_DUPS # Do not record an event that was just recorded again.
setopt HIST_IGNORE_ALL_DUPS # Delete an old recorded event if a new event is a duplicate.
setopt HIST_IGNORE_SPACE # Do not record an event starting with a space.
setopt HIST_SAVE_NO_DUPS # Do not write a duplicate event to the history file.
setopt HIST_VERIFY # Do not execute immediately upon history expansion.
setopt APPEND_HISTORY # append to history file (Default)
setopt HIST_NO_STORE # Don't store history commands
setopt HIST_REDUCE_BLANKS # Remove superfluous blanks from each command line being added to the history.
setopt INTERACTIVE_COMMENTS # Allow comments in interactive shell.
# Example of what I have - you can use your own configuration. Note that this
# example is intended for use on wayland with wl-copy.
export FZF_CTRL_R_OPTS="
--preview 'echo {}' --preview-window up:3:hidden:wrap
--bind 'ctrl-/:toggle-preview'
--bind 'ctrl-y:execute-silent(echo -n {2..} | wl-copy)+abort'
--color header:italic
--header 'Press CTRL-Y to copy command into clipboard'"</code></pre>
</section>
</main>
<footer>Copyright <span class="focus">pfm</span></footer>
</div>
<script>hljs.highlightAll();</script>
</body>
</html>

View file

@ -0,0 +1,160 @@
<!doctype html>
<html>
<head>
<title>Semantic Type Refinement (GS)</title>
<meta name="author" content="pfm" />
<meta name="description" content="semantic type refinement" />
<link rel="stylesheet" type="text/css" href="../gs.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/scala.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
</head>
<body>
<div id="wrapper">
<header><a href="https://garrity.co">λ garrity software</a></header>
<nav>
<ul>
<li><a href="../index.html">about</a></li>
<li><a href="../open-source.html">open source</a></li>
<li><a href="../writing.html">writing</a></li>
<li><a href="../contact.html">contact</a></li>
</ul>
</nav>
<main>
<section>
<h1>Semantic Type Refinement</h1>
<p>Types can be great. In the context of Scala, I like using types heavily.
They help me think and write better code more quickly. The notion of <em>refined types</em>
is a way to leverage types to enforce certain constraints - often at compile time.</p>
<p>Excellent libraries such as
<a class="external" href="https://github.com/fthomas/refined">refined</a>
have an obvious introductory example:</p>
<pre><code class="language-scala">scala> val i2: Int Refined Positive = -5
error: Predicate failed: (-5 > 0).</code></pre>
<p>To be clear: this is great! I want to talk about another way to refine types
without taking away from the standard case.</p>
</section>
<section>
<h2 id="another-approach-to-refinement">Another Approach to Refinement</h2>
<p>The basic refinement example essentially makes the type more specific. Another way to look
at it is that it removes unwanted values from the range of possible values. Rather than a
type that accepts integers, we have a type which only accepts <em>positive</em> integers.</p>
<p>This is useful, though in many context I prefer to go further. At the end of the day,
with this example, I just have a positive integer. What is the purpose of that integer?
Adding specific semantics to a type can make it <em>stronger</em> and <em>more meaningful</em>.</p>
<p>Let's pretend that our positive integer is actually intended to express some configurable
maximum concurrency value (please bear with the exception for now):</p>
<pre><code class="language-scala">opaque type MaximumConcurrency = Int
object MaximumConcurrency:
def apply(candidate: Int): MaximumConcurrency =
if candidate <= 0 then
throw new IllegalArgumentException(
"Maximum concurrency must be 1 or greater."
)
else candidate
given CanEqual[MaximumConcurrency, MaximumConcurrency] = CanEqual.derived
extension (mc: MaximumConcurrency) def toInt(): Int = mc
end MaximumConcurrency</code></pre>
<p>While this involves slightly more typing, it has several benefits:</p>
<ul>
<li><code>MaximumConcurrency</code> obviously describes the purpose of the type.</li>
<li>The type can be independently documented.</li>
<li>Some value of this type can only be equated to other <code>MaximumConcurrency</code> values.</li>
<li>Logic and functions can be contextualized to this type.</li>
<li>Values of this type cannot exist unless they meet validation criteria.</li>
<li>Zero dependencies.</li>
</ul>
<p>Lest we forget the drawbacks:</p>
<ul>
<li>Value validation does not occur at compile time.</li>
<li>Implementation is manual.</li>
</ul>
</section>
<section>
<h2 id="addressing-drawbacks">Addressing Drawbacks</h2>
<p>First off: many values are <em>not</em> known at compile time. Refinement libraries
are not unaware of this, and provide tools for refining at runtime.</p>
<p>Manual implementation simply does not bother me -- it is a small effort, and that
effort <em>forces</em> me to think about each type and document each type. I am also forced
to <em>justify</em> a <em>purpose</em> for each type. I spend more time thinking, and less
time actually writing; a common theme with specific types.</p>
<p>That being said, this approach isn't for everything. Sometimes there are literals that
just need to be non-semantic, because trying to apply that layer has no value. That's okay,
and lines need to be drawn.</p>
</section>
<section>
<h2 id="what-about-exceptions">What About Exceptions?</h2>
<p>There is no reason a type <em>must</em> rely on exceptions to perform validation:</p>
<pre><code class="language-scala">opaque type MaximumConcurrency = Int
object MaximumConcurrency:
def validate(candidate: Int): Either[MyError, MaximumConcurrency] =
if candidate <= 0 then Left(MyError.InvalidMaximumConcurrency(candidate))
else candidate</code></pre>
<p>Refinement of the type can be catered to the case at hand. Use whatever mechanism
best fits the type.</p>
</section>
<section>
<h2 id="types-without-validation">Types Without Validation</h2>
<p>This same approach can be used to give type restrictions to unconstrained values:</p>
<pre><code class="language-scala">opaque type MaximumConcurrency = Int
object MaximumConcurrency:
def apply(value: Int): MaximumConcurrency = value</code></pre>
<p>Opaque type aliases are a technique that I use often, as these types still
prevent the use of mismatched types and communicate valuable information.</p>
</section>
<section>
<h2 id="summary">Summary</h2>
<p>Restrictions are often power, and clarity is also often power. I enjoy both,
and make heavy use of them in my code. In general, I tend to rely on a dependency-free
manual approach to refining my types in a way that forces me to justify the existence
of every single type - I think the approach is at least worth consideration. I think
that libraries which solve a general case well are valuable in their own right and hope
that this brief writeup is not taken as a reason to avoid them.</p>
<p>I particularly like the inclusion of <code>opaque</code> types in Scala 3 and use
them heavily in lieu of fundamental types.</p>
</section>
</main>
<footer>Copyright <span class="focus">pfm</span></footer>
</div>
<script>hljs.highlightAll();</script>
</body>
</html>