Here I am, trying a new thing

May 5, 2024 Trying A New Thing

Dear Cartwheel,

For a long while now I’ve been working up the courage to writing something approximating a blog. You being a cat and all, I wouldn’t expect you to understand my hesitation, let alone my motivation.

But then I see you staring at the ceiling fan, wondering how it is I manipulate light with the pull of a chain, mystified, learning, and I know there is more to you than I give you credit for.

Pretense is counterproductive to writing. But Cartwheel, I have nothing to prove to you.

Here is what I’ve been thinking about lately.

# The Expression Problem

Okay, Cartwheel, you know Haskell, right?

No?

I recently came across a short post that helped me understand the raison d’être for forall types in Haskell.

Imagine you are developing a library and you model your domain with some abstract data types. Perhaps you define some simple shapes:

data Circle = Circle Float Float Float
  deriving (Show, Read, Eq)

data Rectangle = Rectangle Float Float Float Float
  deriving (Show, Read, Eq)

You want to give your users something to do with these shapes. All 2-dimensional shapes have an area, so you write a function to produce the area by pattern-matching on the ADTs and–

area (Circle _ _ r) = pi * r ^ 2
area (Rectangle x y x' y') = (abs $ x - x') * (abs $ y - y')

---- end of file.
---
----- REPL output:
<interactive>:16:7: error:
    • Couldn't match expected type ‘Circle’
                  with actual type ‘Rectangle’
    • In the pattern: Rectangle x y x' y'
      In an equation for ‘area’:
          area (Rectangle x y x' y') = (abs $ x - x') * (abs $ y - y')

Prophet’s post suggested the natural instinct is to define the shapes as components of a sum type, which would make the above code valid at the expense of extensibility. In the interest of allowing us to add more shapes later—or even extend the library in userspace—we should use a typeclass.

This issue of extensibility is part of the so-called “Expression Problem” to which type classes are one solution. I think it is a pretty good solution; writing typeclasses is not too much extra code (it often reduces code!) and they are not too much trouble to understand. They are a nice implementation of a critical idea in programming languages—that data can be associated with behavior in quite complex and overlapping ways.

So you express the idea that all shapes have an area using a typeclass, since this is the idiomatic solution.

class Surface shape where
    surfaceArea :: shape -> Float

instance Surface Circle where
    surfaceArea (Circle _ _ r) = pi * r ^ 2

instance Surface Rectangle where
    surfaceArea (Rectangle x y x' y') = (abs $ x - x') * (abs $ y - y')

---
--- Now entering user-land
---

-- We can extend the library with our own shapes, and still use the same old
-- `surfaceArea` function from the `Surface` typeclass!
data Square = Square Float Float Float
  deriving (Show, Read, Eq)

instance Surface Square where
    surfaceArea (Square x y w) = w ^ 2

But it is still impossible to perform operations over heterogeneous collections of shapes:

-- This looks like it should work, right?
f :: (Surface s) => [s] -> Float
f ss = sum $ map surfaceArea ss

--- REPL:
λ> f [Circle 0 0 5, Rectangle 1 1 2 2]

<interactive>:32:18: error:
    • Couldn't match expected type ‘Circle’
                  with actual type ‘Rectangle’
    • In the expression: Rectangle 1 1 2 2
      In the first argument of ‘f’, namely
        ‘[Circle 0 0 5, Rectangle 1 1 2 2]’
      In the expression: f [Circle 0 0 5, Rectangle 1 1 2 2]

The solution is Existential Types. These types form an opaque, uniform interface to the typeclass methods such that the typechecker is happy with a homogenous collection, but the relevant operations can still be performed.

data WithArea = forall shape. Surface shape => WithArea shape

instance Surface WithArea where
    surfaceArea (WithArea s) = surfaceArea s

--- REPL:
λ> c = Circle 0 0 1
λ> r = Rectangle 0 0 1 1
λ> sum $ map surfaceArea [WithArea c, WithArea r]
79.53982

There is something going on here. Abstract data types, classes, objects, type classes—they all answer the same questions about how to structure our data in modular and extensible ways. Objects are a poor man’s closure, closures are a poor man’s object; and the same holds true for type classes and mixins.

Now I wonder: what programming problem do dependent types solve? And what object-oriented solution was already invented in the 70s that the finest type theorists refuse to acknowledge?

To be honest with you, Cartwheel, I have no quarrel with the type theorists. They are my friends and colleagues, my heroes and mentors. I spend all day writing complex types in Ur/Web. I find the connections between OOP and FP quite fascinating and exciting—beautiful, even.

# Language Feel

I recently wrote a small but complex program in Racket using Geiser in Emacs as my IDE. I usually program in Python or Haskell, but I have a bit of experience in Racket. Cartwheel, these languages feel so different.

Every language has a gimmick or two. Java’s gimmick is garbage-collected objects on a virtual machine. Haskell’s gimmick is laziness and types. Python’s gimmick is dynamism and simplicity. What is Racket’s gimmick? As far as I know, it is macros and languages. Insofar as there is a “right” way to write Racket, the right way is to create small DSLs that help you express solutions to your problem and properly encode the semantics of your domain. I did not do this.

Even with the REPL giving me wonderful affordances, writing Racket felt like floating on air. By contrast, Haskell feels solid, rectilinear, geometric. When I write Haskell, I plan out entire programs by first modeling the types and function signatures and iterating on those before I write a single line of implementation. Haskell makes me feel like an architect, sketching out a blueprint.

Why is this? Working in a richly typed language is like having a conversation with the compiler. The machine assists you, keeps you on track and consistent—but punishes exploration and digression and disorganization. This is a tradeoff I happen to appreciate.

In Racket, I tend to be more hare-brained. I jump around writing little fragments of code, leaving unfinished expressions and throwing out ideas haphazardly. Racket’s homoiconicity and the liveness of the Geiser notebookish interface certainly encouraged this behavior.

Here is another point for the validity of the Sapir-Whorf hypothesis in programming. If we take the phenomenon seriously, can we design languages with intention surrounding the style of programming they encourage?

Programming languages and cognitive science, as fields of study, began at the same time out of the same groups of people thinking about intelligence and human augmentation. Now they feel separated by a wide gap, and we pretend they are not related. I am glad Will Crichton is interested in bringing the insights of cognitive science to programming languages.

Given the stunted state of programming—we appear to be stuck with the assumptions and affordances of the 80s—I fear we are not ready for the revolution in programming heralded by LLMs. I fear the immaturity of our tools for expressing ourselves computationally leaves us unprepared to make full use of the technology of generative AI. This makes it all the more exciting to be working on programming languages right now: the stakes are high for the future of programming.

# Office Spaces

The gruesome banality of suburban office parks holds me with a morbid rapture. Caught in the emptiness between a freakishly large parking lot, a measly green patch with a twiggy tree, and the dull brown brick of a 4-story anonymous office building, I’m searching everywhere for beauty. In its stead, might nostalgia suffice?

In the TV show Adventure Time, villains like Ice King are redeemable, but (spoilers) the final boss is a being of pure chaos. Evil lies on a spectrum with good, implying the ability to become evil from good and good from evil. We all possess some notion of evil. We are capable of understanding evil. Evil is often human. But not chaos. Chaos is unhuman, incomprehensible, the awful marriage of power and ignorance. Evil is drama, but chaos is the tragedy of absurdity. We prefer the wise tyrant to the dangerous fool. For examples of the former acting as defender of the realm against the latter, see Adventure Time’s Ice King, Dark Messiah’s Arantir, Succession’s Logan, and this TV Tropes page.

The modern design trends of offices—the international Airbnb style, the coffee shop consensus, the WeWork—are not evil. They augur nought but chaos, idiocy, dispassion, all that is antithetical to life. Not autocratic narcissistic excess, but a worse fate for society: attrition to the inefficiencies of capitalism without a rational agent which can yoke alien law to human will. A death heralded by the age of average. It is always boring before the end.

How I yearn for the days of olde! When the designers and architects of the mid-century promised technological solutions to social ills. When every office worker was a patient, and Eames and Herman Miller were the doctors dealing in cures.

I’ve been invited to apply to live in MIT’s brand new graduate student dorm, “Graduate Junction” by American Campus Communities. Please, Cartwheel, please tell me who in the world thought I would want to live in a building where the lobby looks like this and this?

Before I go, tell my offices I love them.

# Cozy Sci-Fi

The stark light of a yellow star pierces through the windows of a lonely spaceship, illuminating crumpled bedsheets in the crew quarters, splaying a sunbeam on the pristine cream-colored floor. A bustling market on a remote lunar outpost is host to merchants, criminals, troublemakers and all the rabble dropped there by circumstance and misfortune. Scrappy scavengers laugh over a hearty meal in the mess hall on their old, beaten-up planethopper. Brave explorers on a five-year mission have a moment of respite during shore leave in a Federation solar system. Love blossoms in the vacuum of space. I want to call this cozy sci-fi.

Examples of cozy sci-fi

Please tell me if you know of others.

The essence of cozy sci-fi

I’m sorry, Cartwheel, but you’re just not the right audience for this. I’d really like to think deeply about what qualities define cozy sci-fi, but that kind of research takes time and effort and I’d prefer to publish something sooner.

I can say one thing right now: it is not a difficult leap between the loneliness of space travel and the warmth humans instinctively create out of circumstances of isolation.

Stay tuned for more?

# Further Reading

Cartwheel, I think you should learn to read. You are clearly highly intelligent, and given how you like to attack me when you have the zoomies, you are the apex predator. Why do cats not rule the earth and run society? I suppose they do, in some ways, but let’s be honest: humans did all the work. I wonder if it’s because you cats grow up so quickly. A species with a long time to reach adulthood requires society (or at least a village) to provide supportive structure for both the parents and children to survive. I imagine these phenomena would interact in a positive feedback loop: safety in society permits even longer development, leading to smarter brains and then more society. It seems cats reached a premature local maximum in species evolution.

Well, I have hope for you yet. Here’s some things I’ve been reading so you can learn to be smart like me.

That’s all I have for you.

Good tidings,
Carmel

Top