From 464f925c5cdc8d3a2855c165a04dfb18975baae4 Mon Sep 17 00:00:00 2001 From: Pat Garrity Date: Fri, 26 Sep 2025 21:42:27 -0500 Subject: [PATCH] new website --- src/assets/gs.css | 160 ---------------- src/contact.html | 98 +++++----- src/donate.html | 64 ------- src/gs.css | 220 ++++++++++++++++++++++ src/index.html | 108 ++++++----- src/open-source.html | 64 +++++++ src/oss.html | 143 -------------- src/writing.html | 40 ++++ src/writing/optimizing-shell-history.html | 73 +++++++ src/writing/semantic-type-refinement.html | 160 ++++++++++++++++ 10 files changed, 670 insertions(+), 460 deletions(-) delete mode 100644 src/assets/gs.css delete mode 100644 src/donate.html create mode 100644 src/gs.css create mode 100644 src/open-source.html delete mode 100644 src/oss.html create mode 100644 src/writing.html create mode 100644 src/writing/optimizing-shell-history.html create mode 100644 src/writing/semantic-type-refinement.html diff --git a/src/assets/gs.css b/src/assets/gs.css deleted file mode 100644 index 21ac4b7..0000000 --- a/src/assets/gs.css +++ /dev/null @@ -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; -} diff --git a/src/contact.html b/src/contact.html index d186253..1d7bce1 100644 --- a/src/contact.html +++ b/src/contact.html @@ -1,55 +1,53 @@ - + - - Garrity Software - Contact - - - - - + + Garrity Software - Contact + + + + + + + + +
+
λ garrity software
+ +
+
+

Email

+

pfm@garrity.co

+
-
+
+

Social Media

+

I do not use any social media platforms.

+
- -
-

garrity software

-
+
+

Chat

+

Email me for an invitation to the garrity-software + Zulip organization. + Zulip provides web and desktop clients.

+
- - - - -
- -
-

Contact

-

Community Discord

-

The Garrity Software Discord Community -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.

-

This is also a viable option for getting in touch with Pat directly.

- -

Email

-

pfm@garrity.co is an appropriate email address for reaching out to Pat -directly.

-
- -
- - -
-Content Copyright © Patrick Garrity -
- -
- - +
+

Git

+

All source repositories are currently hosted under + git.garrity.co + (my self-hosted Forgejo instance), + though I may mirror open source repositories on + codeberg.

+
+
+
Copyright pfm
+
+ diff --git a/src/donate.html b/src/donate.html deleted file mode 100644 index f5c0a04..0000000 --- a/src/donate.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - Garrity Software - Donate - - - - - - -
- - -
-

garrity software

-
- - - - - -
- - - - - -
- - -
-Content Copyright © Patrick Garrity -
- -
- - - diff --git a/src/gs.css b/src/gs.css new file mode 100644 index 0000000..25abe17 --- /dev/null +++ b/src/gs.css @@ -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; +} diff --git a/src/index.html b/src/index.html index e85de35..049b286 100644 --- a/src/index.html +++ b/src/index.html @@ -1,53 +1,75 @@ - + - - Garrity Software - About - - - - - + + Garrity Software + + + + + + + + +
+
λ garrity software
+ +
+
+

garrity software (gs) is my software organization. + It is currently focused on open source projects and writing. It is + intended to one day develop software products.

+
-
+
+

Who Am I?

+

pfm [Pat] — Presumably human; + programming enthusiast and professional.

+
- -
-

garrity software

-
+
+

What I Do

+

gs 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.

- - +

For a list of gs projects, + please refer to open source.

- -
-
-

Garrity Software (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.

-

At this point in time, GS is not in a position to release commerical -software. Product development may occur in the future.

-
+

I also write about software from time to time.

-
-

Team

-

Pat Garrity (pfm)

-

Founder and visionary of Garrity Software.

-
-
+

Languages & Ecosystems

+

I primarily work in + Scala 3, + leveraging the Cats ecosystem.

- -
-Content Copyright © Patrick Garrity -
+

My Development Approach

+

I strive to write robust 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.

+
-
+
+

My Goals

+

One day I hope to commit entirely to gs, 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.

- +

If you want to support my goals, discuss software with me in my Zulip organization or + donate if you find my contributions useful and are + in a position to do so.

+
+
+
Copyright pfm
+
+ diff --git a/src/open-source.html b/src/open-source.html new file mode 100644 index 0000000..c97e909 --- /dev/null +++ b/src/open-source.html @@ -0,0 +1,64 @@ + + + + Garrity Software - Open Source + + + + + + + + +
+
λ garrity software
+ +
+
+
    +
  • + gs-uuid +
    + 2025-07-23 + scala + library +
    +
  • +
  • + gs-timing +
    + 2025-07-23 + scala + library +
    +
  • +
  • + gs-datagen +
    + 2025-07-23 + scala + library +
    +
  • +
  • + gs-config +
    + 2025-07-23 + scala + library +
    +
  • +
+
+
+
Copyright pfm
+
+ + diff --git a/src/oss.html b/src/oss.html deleted file mode 100644 index 5c209d6..0000000 --- a/src/oss.html +++ /dev/null @@ -1,143 +0,0 @@ - - - - Garrity Software - Open Source - - - - - - -
- - -
-

garrity software

-
- - - - - -
- -
-

Open Source

-

Garrity Software exposes all non-product software as open source. The MIT -License is standard for GS projects.

- -

If you have questions or want to discuss these projects, consider joining the -Garrity Software Discord Community.

-
- -
-

Version Control (Git)

-

GS uses self-hosted version control at -https://git.garrity.co/garrity-software. -All code is available, though registration and contributions are not accepted at -this time.

- -

Mirrors

-

GS does not currently mirror repositories.

-
- -
-

Maven

-

GS provides a Maven server and does not upload artifacts to other public -Maven/Ivy repositories at this time.

-
resolvers += "garrity-software-gs" at "https://maven.garrity.co/gs"
-

No credentials are required to use this repository.

-
- -
-

API Documentation

-

Code documentation is not yet published publicly.

-
- -
-

Projects

-
- -
gs-uuid - (Scala 3) (Library)
-
Uses JUG - and ported code from Jackson to provide an opaque UUID type - over java.util.UUID, backed by custom rendering and parsing.
- -
gs-slug - (Scala 3) (Library)
-
Provides a Slug type. This type relies on a very small set of - ASCII characters and is intended for URL-safe identifiers.
- -
gs-hex - (Scala 3) (Library)
-
Small, efficient, Hexadecimal conversion library with encoder/decoder type - classes and support for basic types. Based on handling byte arrays.
- -
gs-blob - (Scala 3) (Library)
-
Opaque type (Blob) and tools (e.g. encoding) for "blobs" - - arrays of bytes.
- -
gs-datagen - (Scala 3) (Library)
-
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.
- -
gs-config - (Scala 3) (Library)
-
Library for loading, and specifically auditing, configuration. - Provides a complete digest of what configuration keys were queried and what - happened to them.
- -
-
- -
-

Incubator

-
- -
gs-test - (Scala 3) (Library)
-
Test framework for Scala 3. Based on Cats Effect. Notably provides - structured test output and envisions a more powerful method of expressing - test execution.
- -
gs-log - (Scala 3) (Library)
-
FP logging library for Scala 3.
- -
gs-crypto - (Scala 3) (Library)
-
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.
- -
smolban - (Scala 3) (Application)
-
Fun side-project to create a small/minimal Kanban oriented system. Light on - process, helps prove out certain concepts in a non-critical - application.
- -
-
- -
- - -
-Content Copyright © Patrick Garrity -
- -
- - - diff --git a/src/writing.html b/src/writing.html new file mode 100644 index 0000000..1aa092f --- /dev/null +++ b/src/writing.html @@ -0,0 +1,40 @@ + + + + Garrity Software - Writing + + + + + + + + +
+
λ garrity software
+ +
+
+ +
+
+
Copyright pfm
+
+ + diff --git a/src/writing/optimizing-shell-history.html b/src/writing/optimizing-shell-history.html new file mode 100644 index 0000000..d4ab5a3 --- /dev/null +++ b/src/writing/optimizing-shell-history.html @@ -0,0 +1,73 @@ + + + + Garrity Software - Optimizing Shell History + + + + + + + + +
+
λ garrity software
+ +
+
+

Optimizing Shell History

+

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.

+

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.

+

I take advantage of three things:

+
    +
  1. History configuration settings.
  2. +
  3. Shell comments.
  4. +
  5. Fuzzy search (FZF, in this case).
  6. +
+
+
+

History Configuration

+

I currently use zsh, so the example below is + for zsh.

+
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'"
+
+
+
Copyright pfm
+
+ + + diff --git a/src/writing/semantic-type-refinement.html b/src/writing/semantic-type-refinement.html new file mode 100644 index 0000000..2483637 --- /dev/null +++ b/src/writing/semantic-type-refinement.html @@ -0,0 +1,160 @@ + + + + Semantic Type Refinement (GS) + + + + + + + + + + + +
+
λ garrity software
+ +
+
+

Semantic Type Refinement

+

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 refined types + is a way to leverage types to enforce certain constraints - often at compile time.

+

Excellent libraries such as + refined + have an obvious introductory example:

+
scala> val i2: Int Refined Positive = -5
+       error: Predicate failed: (-5 > 0).
+

To be clear: this is great! I want to talk about another way to refine types + without taking away from the standard case.

+
+ +
+

Another Approach to Refinement

+ +

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 positive integers.

+ +

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 stronger and more meaningful.

+ +

Let's pretend that our positive integer is actually intended to express some configurable + maximum concurrency value (please bear with the exception for now):

+ +
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
+ +

While this involves slightly more typing, it has several benefits:

+ +
    +
  • MaximumConcurrency obviously describes the purpose of the type.
  • +
  • The type can be independently documented.
  • +
  • Some value of this type can only be equated to other MaximumConcurrency values.
  • +
  • Logic and functions can be contextualized to this type.
  • +
  • Values of this type cannot exist unless they meet validation criteria.
  • +
  • Zero dependencies.
  • +
+ +

Lest we forget the drawbacks:

+ +
    +
  • Value validation does not occur at compile time.
  • +
  • Implementation is manual.
  • +
+ +
+ +
+

Addressing Drawbacks

+ +

First off: many values are not known at compile time. Refinement libraries + are not unaware of this, and provide tools for refining at runtime.

+ +

Manual implementation simply does not bother me -- it is a small effort, and that + effort forces me to think about each type and document each type. I am also forced + to justify a purpose for each type. I spend more time thinking, and less + time actually writing; a common theme with specific types.

+ +

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.

+ +
+ +
+

What About Exceptions?

+ +

There is no reason a type must rely on exceptions to perform validation:

+ +
opaque type MaximumConcurrency = Int
+
+object MaximumConcurrency:
+
+  def validate(candidate: Int): Either[MyError, MaximumConcurrency] =
+    if candidate <= 0 then Left(MyError.InvalidMaximumConcurrency(candidate))
+    else candidate
+ +

Refinement of the type can be catered to the case at hand. Use whatever mechanism + best fits the type.

+ +
+ +
+

Types Without Validation

+ +

This same approach can be used to give type restrictions to unconstrained values:

+ +
opaque type MaximumConcurrency = Int
+
+object MaximumConcurrency:
+
+  def apply(value: Int): MaximumConcurrency = value
+ +

Opaque type aliases are a technique that I use often, as these types still + prevent the use of mismatched types and communicate valuable information.

+
+ +
+

Summary

+ +

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.

+ +

I particularly like the inclusion of opaque types in Scala 3 and use + them heavily in lieu of fundamental types.

+
+
+
Copyright pfm
+
+ + +